[UIKit] 텍스트필드 입력(클릭) 시 키보드 올리기/내리기 (3): Rxswift

2024. 11. 12. 10:49iOS/UIKit

728x90
반응형

🧸 시작

2024.11.08 - [SWIFT/UIKit] - [UIKit] 텍스트필드 입력(클릭) 시 키보드 올리기/내리기 (2): Snpakit

 

[UIKit] 텍스트필드 입력(클릭) 시 키보드 올리기/내리기 (2): Snpakit

🧸 시작이전 글에서는 스토리보드를 활용해 키보드가 올라오거나 내려갈 때마다 제약조건을 변경해 UI를 조정하는 방법을 사용했다. 이번 글에서는 코드 기반의 접근 방법으로 동일한 작업을

nlestory.tistory.com

 

이전 글에서는 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 업데이트와 관련된 키보드 이벤트에 적합하다.

 

 

 

 

반응형
728x90

 

 

 

 

📂 정리

키보드 이벤트를 Rx를 이용하여 처리하는 방법은 기존의 NotificationCenter 기반의 처리 방식에서 코드가 간결해지고 가독성이 높아져서 UI와 키코드의 동기화 상태를 직관적으로 구성할 수 있었다.

다양한 메모리 관리 방법을 통해 안정적인 코드를 작성하고 debug()를 통해 구독 이벤트 흐름을 디버깅할 수 있다. Rx를 이용한 구독은 반응형 프로그래밍의 장점을 활용하여 안정적인 UI 상호작용을 제공한다. 이러한 방식으로 코드 작성 시 코드 유지보수성을 높이고 애플리케이션 성능을 개선하는 효과를 얻을 수 있다.

 

728x90
반응형