카테고리 없음

Swift - 키보드의 높이를 알아내는 법🚀

ragdoll-cat 2025. 3. 1. 00:00

앱 개발 중 TextField 또는 TextEditor와 같이 텍스트 입력 필드를 사용할 때, 자연스럽게 키보드가 화면 위로 올라오게 됩니다.

키보드가 올라온 화면까지 고려해서 디자인과 기능을 구현하기도 하고 또 구현 의도에 따라 키보드가 화면을 가리지 않도록 적절한 처리가 필요한 경우가 있습니다. 이를 위해 키보드의 높이를 감지하는 방법을 알아봅시다.


1️⃣ NotificationCenter를 사용하여 키보드 높이 구하기

📌 키보드 이벤트 감지하기

키보드의 높이를 알아내기 위해서는 키보드가 나타나거나 사라질 때 발생하는 Notification을 활용할 수 있습니다. iOS에서는 다음과 같은 키보드 관련 Notification을 제공합니다.

UIKeyboardWillShowNotification : 키보드가 나타날 때 발생
UIKeyboardWillHideNotification : 키보드가 사라질 때 발생
UIKeyboardWillChangeFrameNotification : 키보드의 프레임이 변경될 때 발생

이 이벤트들을 활용하면 키보드의 상태 변화를 실시간으로 감지할 수 있습니다.

🎯 Notification에서 키보드 높이 가져오기

키보드 높이는 Notification에서 제공하는 userInfo 딕셔너리를 통해 확인할 수 있습니다.

UIKeyboardFrameEndUserInfoKey 키를 사용하여 최종 프레임 값을 가져오면, 키보드의 높이를 쉽게 얻을 수 있어요.

guard let keyboardInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
  return
}
let keyboardSize = keyboardInfo.cgRectValue.size
let keyboardHeight = keyboardSize.height // Keyboard 높이

📌 키보드 높이 활용하기

키보드의 높이를 알게 되면, 키보드의 유무에 따른 화면의 변화를 자유롭게 만들 수 있습니다. 예를 들어, 입력 필드가 화면 하단에 위치할 경우 키보드의 높이만큼 여백을 증가시켜 뷰를 올리는 방식을 적용할 수 있습니다.

🎯 키보드가 사라질 때의 처리

키보드가 사라지면, 뷰의 위치를 원래 상태로 되돌려야 한다. 이를 위해 UIKeyboardWillHideNotification을 감지하여 뷰를 초기 위치로 복원하면 됩니다.

‼️주의

NotificationCenter를 사용할 때 removeObserver를 호출해주지 않으면 메모리에 남아있게 되니 주의해야 합니다.

 

  • KeyboardObserver 전체 코드
import SwiftUI

class KeyboardObserver: ObservableObject {
  
  private var notificationCenter: NotificationCenter
  
  @Published var isShowing = false
  @Published var height: CGFloat = 0
  
  init(center: NotificationCenter = .default) {
    notificationCenter = center
    
    addObserver()
  }
  
  deinit {
    removeObserver()
  }
  
  func addObserver() {
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
  }
  
  func removeObserver() {
    NotificationCenter.default.removeObserver(self,name: UIResponder.keyboardWillShowNotification,object: nil)
    NotificationCenter.default.removeObserver(self,name: UIResponder.keyboardWillHideNotification,object: nil)
  }
  
  @objc func keyboardWillShow(_ notification: Notification) {
    isShowing = true
    guard let userInfo = notification.userInfo as? [String: Any] else {
      return
    }
    guard let keyboardInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
      return
    }
    let keyboardSize = keyboardInfo.cgRectValue.size
    height = keyboardSize.height
  }
  
  @objc func keyboardWillHide(_ notification: Notification) {
    isShowing = false
    height = 0
  }
}
  • 사용하는 View에서의 예시
import SwiftUI

struct ContentView: View {
  @StateObject var keyboard: KeyboardObserver = KeyboardObserver()
  
  var body: some View {
    VStack {
      ...
      Spacer()
        .frame(height: keyboard.isShowing ? keyboard.height : 0)
    }
  }
}

2️⃣ Combine 사용하기

사실 위의 방법과 원리는 똑같습니다. 키보드 관련 Notification을 통해 상태를 확인하고, UIKeyboardFrameEndUserInfoKey 키를 통해 키보드의 Size와 높이를 알아낼 수 있습니다.

import SwiftUI
import Combine

class KeyboardObserver: ObservableObject {
  
  @Published var isShowing = false
  @Published var height: CGFloat = 0
  
  private var cancellable = Set<AnyCancellable>()
  
  init() {
    bindKeyboardPublisher()
  }
  
  private func bindKeyboardPublisher() {
    let keyboardWillShowPublisher = NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)
          .map { notification -> CGFloat in
            guard let userInfo = notification.userInfo as? [String: Any] else {
              return .zero
            }
            guard let keyboardInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
              return .zero
            }
            let keyboardSize = keyboardInfo.cgRectValue.size
            return keyboardSize.height
          }
    let keyboardWillHidePublisher = NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)
          .map { _ in CGFloat(0) }
    
    Publishers.Merge(keyboardWillShowPublisher, keyboardWillHidePublisher)
      .receive(on: RunLoop.main)
      .sink { [weak self] height in
        self?.height = height
        self?.isShowing = height > 0
      }
      .store(in: &cancellable)
  }
}

 

개인의 스타일에 따라 작성 방법의 차이인 것 같습니다!