
댓글 입력을 위해 TextField 터치 시, 키보드가 TextField를 가려 사용자가 무엇을 입력하는지 알 수 없다. 키보드 위로 TextField가 올라왔으면 좋겠는데 어떻게 하면 좋을까?
생각하기
1. 키보드 터치 이벤트를 감지해서 레이아웃을 수정하면 되지 않을까?
2. 키보드 터치 이벤트를 감지하는 방법에는 뭐가 있을까?
✅ Notification Center? textField의 textFieldDidChange? textField.rx.controlEvent?
이벤트 감지
✔️textFieldDidChange은 TextField의 특정 이벤트에 대한 리액션을 처리하는데 사용된다. textFieldDidChange는 textField의 텍스트가 변경됐을 때의 이벤트를 처리한다. 이러한 이벤트는 키보드의 나타남이나 사라짐과 직접적으로 관련이 없다.
✔️ textField.rx.controlEvent는 TextField의 이벤트를 처리하는 데 사용된다. 키보드의 나타남과 사라짐은 TextField의 입력과 직접적인 연관성이 없다.
// 텍스트 필드의 becomeFirstResponder() 메서드를 호출하여 키보드를 나타나게 함
textField.becomeFirstResponder()
// RxSwift를 사용하여 textField.rx.controlEvent를 감시
textField.rx.controlEvent([.editingDidBegin]) // 텍스트 필드에 입력이 시작될 때
.subscribe(onNext: { [weak self] in
// 키보드가 나타날 때 필요한 작업 수행
})
.disposed(by: disposeBag)
// 텍스트 필드의 resignFirstResponder() 메서드를 호출하여 키보드를 숨김
textField.resignFirstResponder()
// RxSwift를 사용하여 textField.rx.controlEvent를 감시
textField.rx.controlEvent([.editingDidEndOnExit]) // 텍스트 필드에서 입력이 종료될 때
.subscribe(onNext: { [weak self] in
// 키보드가 사라질 때 필요한 작업 수행
})
.disposed(by: disposeBag)
물론 억지스럽게 어떻게든 구현할 수는 있지만, 일반적으로 권장되는 방법은 아니다. 키보드의 나타남과 사라짐은 시스템 이벤트로, 일반적으로 Notification Center의 특정 알림을 사용하여 처리한다. 이러한 시스템 이벤트를 효율적으로 처라하기 위해서는 해당 이벤트를 직접 관찰하고 처리해야한다.
위와 같은 이유로 Notification Center로 구현해보기로 했다.

이벤트를 감지하기 위해서는 "NotificationCenter"를 사용하면 된다. (이번 프로젝트는 rx기반이기 때문에 rx로 처리해보자.)
✅ 키보드가 나타날 때
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
.subscribe(onNext: { [weak self] notification in
self?.handleKeyboardWillShow(notification)
})
.disposed(by: disposeBag)
keyboardWillShowNotification을 사용해서 키보드가 나타날 때 이벤트를 처리한다.
✅ 키보드가 사라질 때
NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)
.subscribe(onNext: { [weak self] notification in
self?.handleKeyboardWillHide(notification)
})
.disposed(by: disposeBag)
keyboardWillHideNotification을 사용해서 키보드가 사라질 때 이벤트를 처리한다.
키보드가 나타날 때와 사라질 때 이벤트 감지 코드를 작성했으니, 이벤트를 감지하면 실행 할 메소드를 작성해보자.
일단 로직을 생각해보자.
1. 키보드의 크기와 키보드가 나타날 때의 애니메이션 지속시간을 알아온다.
✅ 키보드의 크기와 애니메이션 지속시간은 사용자가 어떤 기기를 사용하는지에 따라 다 다르니 고정 된 값이 아니라, notification의 userInfo를 사용해서 알아온다.
✅ 키보드가 나타날 때의 애니메이션 지속시간은 왜 알아야하는가?
✔️ 키보드가 나타날 때 발생하는 애니메이션과 함께 다른 UI 요소들도 동일한 타이밍으로 애니메이션 되도록 하기 위함. ➡️ 시각적 일관성
✔️ 키보드가 나타날 때 입력 필드나 버튼이 가려지는 것을 방지하기 위해, 해당 UI 요소들을 키보드가 나타나는 것과 동시에 위로 이동시키기 위함
2. 애니메이션을 이용해서 view의 레이아웃을 업데이트 한다.
private func handleKeyboardWillShow(_ notification: Notification) {
// 알림에서 키보드의 정보 가져오기
guard let userInfo = notification.userInfo,
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
let keyboardHeight = keyboardFrame.height
// 애니메이션을 사용하여 footerView 위치 업데이트
UIView.animate(withDuration: animationDuration) {
self.footerView.snp.updateConstraints {
$0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(keyboardHeight)
}
self.view.layoutIfNeeded()
}
}
키보드가 사라질 때도 위와 동일하게 구현해준다.
private func handleKeyboardWillHide(_ notification: Notification) {
// 알림에서 애니메이션 지속 시간 가져오기
guard let userInfo = notification.userInfo,
let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
// 애니메이션을 사용하여 footerView 위치 복원
UIView.animate(withDuration: animationDuration) {
self.footerView.snp.updateConstraints {
$0.bottom.equalTo(self.view.safeAreaLayoutGuide)
}
self.view.layoutIfNeeded()
}
}
✅ 키보드가 사라질 때 키보드의 높이가 필요 없는 이유 : 키보드가 화면에서 내려갈 때 View의 레이아웃을 원래 상태로 복원하기 때문이다. 즉, 키보드가 나타날 때는 키보드 높이만큼 View를 올려야하지만, 키보드가 사라질 때는 원래 위치로 복원하면 되므로 키보드의 높이를 참조할 필요가 없기 때문이다.

정리
키보드가 나타날 때
1. 키보드의 높이를 구한다.
2. 키보드 높이만큼 View를 올려준다.
3. 이 동작을 애니메이션으로 처리한다.
키보드가 사라질 때
1. View의 위치를 원래 상태로 복원한다.
2. 이 동작을 애니메이션으로 처리한다.
✅ 애니메이션 지속 시간 : 애니메이션의 부드러움과 시각적 일관성을 유지하기 위해 필요하다.
✅ 즉시 레이아웃 업데이트 : layoutIfNeeded() 호출
✅ 키보드가 사라질 때는 키보드 높이를 고려할 필요가 없다.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// 키보드 노티피케이션 등록
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(_ notification: Notification) {
guard let userInfo = notification.userInfo else { return }
guard let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
guard let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
// 키보드 높이만큼 뷰를 위로 올립니다.
UIView.animate(withDuration: animationDuration) {
self.bottomConstraint.constant = keyboardFrame.height
self.view.layoutIfNeeded() // 즉시 레이아웃을 업데이트합니다.
}
}
@objc func keyboardWillHide(_ notification: Notification) {
guard let userInfo = notification.userInfo else { return }
guard let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
// 원래 위치로 돌아갑니다.
UIView.animate(withDuration: animationDuration) {
self.bottomConstraint.constant = 0
self.view.layoutIfNeeded() // 즉시 레이아웃을 업데이트합니다.
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Rx 없이 UIKit으로 구현한 간단한 예제이다. Rx에서는 dispose를 통해 메모리에서 해제를 해주었지만, UIKit에서는 deinit으로 옵저버를 remove하여 메모리를 관리한다.
'ToyProject - 사카마카 (살까말까 고민 될 때는 사카마카)' 카테고리의 다른 글
[사카마카/회고] 사카마카 개발을 마무리 하며 (1) | 2024.06.11 |
---|---|
[사카마카] TableView의 Section을 이용해서 대댓글 기능을 구현해보자. (0) | 2024.06.03 |
[사카마카/문제해결] 앱 내에서 웹을 보여줄 때 발생하는 스킴 문제를 해결해보자. (0) | 2024.05.30 |
[사카마카] 앱 내에서 웹을 보여주는 방법에 대해 알아보자. (0) | 2024.05.30 |
[사카마카] Lottie로 애니메이션을 사용해보자. (0) | 2024.05.29 |

댓글 입력을 위해 TextField 터치 시, 키보드가 TextField를 가려 사용자가 무엇을 입력하는지 알 수 없다. 키보드 위로 TextField가 올라왔으면 좋겠는데 어떻게 하면 좋을까?
생각하기
1. 키보드 터치 이벤트를 감지해서 레이아웃을 수정하면 되지 않을까?
2. 키보드 터치 이벤트를 감지하는 방법에는 뭐가 있을까?
✅ Notification Center? textField의 textFieldDidChange? textField.rx.controlEvent?
이벤트 감지
✔️textFieldDidChange은 TextField의 특정 이벤트에 대한 리액션을 처리하는데 사용된다. textFieldDidChange는 textField의 텍스트가 변경됐을 때의 이벤트를 처리한다. 이러한 이벤트는 키보드의 나타남이나 사라짐과 직접적으로 관련이 없다.
✔️ textField.rx.controlEvent는 TextField의 이벤트를 처리하는 데 사용된다. 키보드의 나타남과 사라짐은 TextField의 입력과 직접적인 연관성이 없다.
// 텍스트 필드의 becomeFirstResponder() 메서드를 호출하여 키보드를 나타나게 함
textField.becomeFirstResponder()
// RxSwift를 사용하여 textField.rx.controlEvent를 감시
textField.rx.controlEvent([.editingDidBegin]) // 텍스트 필드에 입력이 시작될 때
.subscribe(onNext: { [weak self] in
// 키보드가 나타날 때 필요한 작업 수행
})
.disposed(by: disposeBag)
// 텍스트 필드의 resignFirstResponder() 메서드를 호출하여 키보드를 숨김
textField.resignFirstResponder()
// RxSwift를 사용하여 textField.rx.controlEvent를 감시
textField.rx.controlEvent([.editingDidEndOnExit]) // 텍스트 필드에서 입력이 종료될 때
.subscribe(onNext: { [weak self] in
// 키보드가 사라질 때 필요한 작업 수행
})
.disposed(by: disposeBag)
물론 억지스럽게 어떻게든 구현할 수는 있지만, 일반적으로 권장되는 방법은 아니다. 키보드의 나타남과 사라짐은 시스템 이벤트로, 일반적으로 Notification Center의 특정 알림을 사용하여 처리한다. 이러한 시스템 이벤트를 효율적으로 처라하기 위해서는 해당 이벤트를 직접 관찰하고 처리해야한다.
위와 같은 이유로 Notification Center로 구현해보기로 했다.

이벤트를 감지하기 위해서는 "NotificationCenter"를 사용하면 된다. (이번 프로젝트는 rx기반이기 때문에 rx로 처리해보자.)
✅ 키보드가 나타날 때
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
.subscribe(onNext: { [weak self] notification in
self?.handleKeyboardWillShow(notification)
})
.disposed(by: disposeBag)
keyboardWillShowNotification을 사용해서 키보드가 나타날 때 이벤트를 처리한다.
✅ 키보드가 사라질 때
NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)
.subscribe(onNext: { [weak self] notification in
self?.handleKeyboardWillHide(notification)
})
.disposed(by: disposeBag)
keyboardWillHideNotification을 사용해서 키보드가 사라질 때 이벤트를 처리한다.
키보드가 나타날 때와 사라질 때 이벤트 감지 코드를 작성했으니, 이벤트를 감지하면 실행 할 메소드를 작성해보자.
일단 로직을 생각해보자.
1. 키보드의 크기와 키보드가 나타날 때의 애니메이션 지속시간을 알아온다.
✅ 키보드의 크기와 애니메이션 지속시간은 사용자가 어떤 기기를 사용하는지에 따라 다 다르니 고정 된 값이 아니라, notification의 userInfo를 사용해서 알아온다.
✅ 키보드가 나타날 때의 애니메이션 지속시간은 왜 알아야하는가?
✔️ 키보드가 나타날 때 발생하는 애니메이션과 함께 다른 UI 요소들도 동일한 타이밍으로 애니메이션 되도록 하기 위함. ➡️ 시각적 일관성
✔️ 키보드가 나타날 때 입력 필드나 버튼이 가려지는 것을 방지하기 위해, 해당 UI 요소들을 키보드가 나타나는 것과 동시에 위로 이동시키기 위함
2. 애니메이션을 이용해서 view의 레이아웃을 업데이트 한다.
private func handleKeyboardWillShow(_ notification: Notification) {
// 알림에서 키보드의 정보 가져오기
guard let userInfo = notification.userInfo,
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
let keyboardHeight = keyboardFrame.height
// 애니메이션을 사용하여 footerView 위치 업데이트
UIView.animate(withDuration: animationDuration) {
self.footerView.snp.updateConstraints {
$0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(keyboardHeight)
}
self.view.layoutIfNeeded()
}
}
키보드가 사라질 때도 위와 동일하게 구현해준다.
private func handleKeyboardWillHide(_ notification: Notification) {
// 알림에서 애니메이션 지속 시간 가져오기
guard let userInfo = notification.userInfo,
let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
// 애니메이션을 사용하여 footerView 위치 복원
UIView.animate(withDuration: animationDuration) {
self.footerView.snp.updateConstraints {
$0.bottom.equalTo(self.view.safeAreaLayoutGuide)
}
self.view.layoutIfNeeded()
}
}
✅ 키보드가 사라질 때 키보드의 높이가 필요 없는 이유 : 키보드가 화면에서 내려갈 때 View의 레이아웃을 원래 상태로 복원하기 때문이다. 즉, 키보드가 나타날 때는 키보드 높이만큼 View를 올려야하지만, 키보드가 사라질 때는 원래 위치로 복원하면 되므로 키보드의 높이를 참조할 필요가 없기 때문이다.

정리
키보드가 나타날 때
1. 키보드의 높이를 구한다.
2. 키보드 높이만큼 View를 올려준다.
3. 이 동작을 애니메이션으로 처리한다.
키보드가 사라질 때
1. View의 위치를 원래 상태로 복원한다.
2. 이 동작을 애니메이션으로 처리한다.
✅ 애니메이션 지속 시간 : 애니메이션의 부드러움과 시각적 일관성을 유지하기 위해 필요하다.
✅ 즉시 레이아웃 업데이트 : layoutIfNeeded() 호출
✅ 키보드가 사라질 때는 키보드 높이를 고려할 필요가 없다.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// 키보드 노티피케이션 등록
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(_ notification: Notification) {
guard let userInfo = notification.userInfo else { return }
guard let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
guard let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
// 키보드 높이만큼 뷰를 위로 올립니다.
UIView.animate(withDuration: animationDuration) {
self.bottomConstraint.constant = keyboardFrame.height
self.view.layoutIfNeeded() // 즉시 레이아웃을 업데이트합니다.
}
}
@objc func keyboardWillHide(_ notification: Notification) {
guard let userInfo = notification.userInfo else { return }
guard let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
// 원래 위치로 돌아갑니다.
UIView.animate(withDuration: animationDuration) {
self.bottomConstraint.constant = 0
self.view.layoutIfNeeded() // 즉시 레이아웃을 업데이트합니다.
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Rx 없이 UIKit으로 구현한 간단한 예제이다. Rx에서는 dispose를 통해 메모리에서 해제를 해주었지만, UIKit에서는 deinit으로 옵저버를 remove하여 메모리를 관리한다.
'ToyProject - 사카마카 (살까말까 고민 될 때는 사카마카)' 카테고리의 다른 글
[사카마카/회고] 사카마카 개발을 마무리 하며 (1) | 2024.06.11 |
---|---|
[사카마카] TableView의 Section을 이용해서 대댓글 기능을 구현해보자. (0) | 2024.06.03 |
[사카마카/문제해결] 앱 내에서 웹을 보여줄 때 발생하는 스킴 문제를 해결해보자. (0) | 2024.05.30 |
[사카마카] 앱 내에서 웹을 보여주는 방법에 대해 알아보자. (0) | 2024.05.30 |
[사카마카] Lottie로 애니메이션을 사용해보자. (0) | 2024.05.29 |