2024. 11. 12. 10:49ㆍiOS/UIKit
🧸 시작
2024.11.08 - [SWIFT/UIKit] - [UIKit] 텍스트필드 입력(클릭) 시 키보드 올리기/내리기 (2): Snpakit
이전 글에서는 Snapkit과 NotificationCenter를 사용하여 키보드 이벤트를 처리하는 방법을 다뤘다면, 이번에는 RxSwift로 이 과정을 좀 더 간결하고 반응형으로 구현하는 방법을 구현할 예정이다.
RxSwift를 활용하면 NotificationCenter를 통해 키보드 이벤트를 구독하고, 더 나아가 반응형 프로그래밍의 장점을 활용하여 UI와 키보드 상태 간의 동기화를 보다 유연하게 할 수 있다. 이를 통해 코드 가독성을 높이고, 복잡한 콜백 처리 없이 직관적으로 키보드와 UI의 상호작용을 구성하는 데 중점을 두었다.
이번 글에서는 RxSwift를 이용하여 텍스트 필드와 키보드의 이벤트를 관리하는 방법을 살펴볼 것이다.
🧸 RxSwift 설치
먼저 RxSwift를 사용하기 위해서 SPM을 사용하여 설치한다.
File > Add Package Dependencies...
다른 라이브러리는 사용하지 않을 거라 RxCocoa 만 추가해준다.
🧸 키보드 옵저버 Observable
전에 사용하던 NotificationCenter와 관련된 코드를 제거하고 RxSwift를 이용해 NotificationCenter의 키보드 이벤트를 Observable로 변환하여 구독하는 방식으로 코드를 수정한다.
import RxSwift
import RxCocoa
extension ViewController {
private func configureKeyboardNotifications() {
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
.compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect }
.map { $0.height }
.subscribe(onNext: { [weak self] keyboardHeight in
guard let self = self else { return }
print("✍️ 키보드 나타나기~")
self.containerView.snp.updateConstraints {
$0.centerY.equalToSuperview().offset(-(keyboardHeight / 2))
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
})
.disposed(by: disposeBag)
NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)
.subscribe(onNext: { [weak self] _ in
guard let self = self else { return }
print("👋 키보드 사라지기~")
self.containerView.snp.updateConstraints {
$0.centerY.equalToSuperview()
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
})
.disposed(by: disposeBag)
}
}
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification) 구문을 사용하여 keyboardWillShowNotification 을 Observable로 변환했다.
compactMap 을 통해 안전하게 키보드의 높이를 추출하고 map을 통해 height만 사용하도록 처리했다.
✔️ DisposeBag
RxSwift에서 구독한 후 해당 구독이 해제되지 않으면 메모리 누수가 발생할 수 있다. 특히 뷰컨트롤러에서 키보드 관련 Observable을 구독할 때에는 disposeBag을 이용하여 구독을 명확하게 해제하는 것이 중요하다. disposed(by: disposeBag)을 사용하여 뷰가 해제될 때 자동으로 구독을 해제하게 한다.
✔️ [weak self]
Observable 클로저 내부에서 self를 사용할 때는 강한 참조 순환을 방지하기 위해 [weak self]를 사용해야한다. 뷰컨트롤러가 해제될 때 해당 구독이 자동으로 해제되어 메모리 누수를 방지할 수 있다.
✔️ 에러 핸들링
키보드에서는 큰 에러가 발생하지 않지만 만약의 에러가 나올 상황에 대비하여 catchAndReturn이나 catch 같은 연산자를 사용할 수 있다. catchAndReturn를 사용하여 에러가 발생할 경우 기본값을 리턴하거나 catch { error in Observable.empty() }를 사용하여 에러가 발생할 경우 빈 옵저버블을 반환하도록 설정하면 좋다.
✔️ 디버그 로그 활용
debug() 를 활용하면 옵저버블의 수명 주기 동안 발생하는 모든 이벤트를 콘솔에 출력할 수 있어서 쿠독 이벤트가 올바르게 발생하고 있는 확인하는 데 도움을 준다.
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
.debug("키보드 이벤트")
.compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect }
.map { $0.height }
.subscribe(onNext: { keyboardHeight in
print("키보드 높이: \(keyboardHeight)")
})
.disposed(by: disposeBag)
이런 식으로 옵저버블의 라이플사이클을 파악하고 이벤트가 올바르게 전달되었는지 확인할 수 있어 디버깅에 매우 유용하다. Rx를 사용하다보면 디버그가 굉장히 어렵다고 느껴질 것이다. 보통 디버그를 할 때면 브레이크 포인트를 잡고 스텝을 넘기는 방식으로 하다보면 라이브러리 안으로 파헤치다가 정작나의 흐름을 놓칠 때가 있다. 디버그에 대한 부분은 다른 글에서 한번 더 정리해야겠다.
✔️ bind 와 drive
bind와 drive는 메인 스레드에서 실행되어 UI업데이트할 때에 안전해서 UI관련 이벤트에 주로 사용하는 연산자이다. drive는 에러가 발생해도 drive.empty() 로 구독이 끊어지지 않기때문에 UI 업데이트와 관련된 키보드 이벤트에 적합하다.
📂 정리
키보드 이벤트를 Rx를 이용하여 처리하는 방법은 기존의 NotificationCenter 기반의 처리 방식에서 코드가 간결해지고 가독성이 높아져서 UI와 키코드의 동기화 상태를 직관적으로 구성할 수 있었다.
다양한 메모리 관리 방법을 통해 안정적인 코드를 작성하고 debug()를 통해 구독 이벤트 흐름을 디버깅할 수 있다. Rx를 이용한 구독은 반응형 프로그래밍의 장점을 활용하여 안정적인 UI 상호작용을 제공한다. 이러한 방식으로 코드 작성 시 코드 유지보수성을 높이고 애플리케이션 성능을 개선하는 효과를 얻을 수 있다.
'iOS > UIKit' 카테고리의 다른 글
[UIkit] 이미지 사이즈에 따른 동적 컬렉션뷰 셀 (0) | 2025.02.03 |
---|---|
[알고리즘] Swift -프로그래머스 연습문제 #258712(카카오 인턴십_가장 많이 받은 선물) (0) | 2024.11.13 |
[UIKit] 텍스트필드 입력(클릭) 시 키보드 올리기/내리기 (2): Snpakit (0) | 2024.11.08 |
[UIkit] Xcode pbxproj 충돌 해결방법 (0) | 2024.05.20 |
[UIKit] Thread 16: "*** +[NSURLComponents setPercentEncodedQueryItems:]: invalid characters in percentEncodedQueryItems" (0) | 2023.05.06 |