티스토리 뷰

728x90
반응형

RxSwift를 사용하지 않은 KeyboardNotification 다루기

 

 

개발을 하다보면, TextField를 선택하였을 때 해당 TextField영역을 KeyBoard가 침범하여

[1] 작성하는 내용

[2] 완료 버튼

등을 가리는 이슈가 발생하곤 합니다.

해당 이슈를 해결하기 위해서는 Notification이라는 Class를 사용해야합니다.

 

UIScrollView 내부에 정의되어 있는 TextField의 경우에는 아래의 코드를 작성하면 

TextField를 선택 하였을 때, KeyBoard의 높이만큼 ScrollView가 올라가게 만들 수 있습니다.

class SignUpViewController: BaseViewController{

    var keyBoardUpDown = false
    
    override func viewDidAppear(_ animated: Bool){
        self.setKeyboardObserver()
	}

	override func viewDidDisappear(_ animated: Bool) {
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    

    func setKeyboardObserver(){
        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: NSNotification) {
        
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if !keyBoardUpDown {
                return
            }
            if self.view.frame.origin.y == 0 {
                self.scrollView.contentOffset.y += keyboardSize.height
            }
        }
    }
    
    @objc func keyboardWillHide(notification: NSNotification) {
        if self.scrollView.contentOffset.y != 0 {
            self.scrollView.contentOffset.y = 0
            keyBoardUpDown = false
        }
    }
    
    @objc func MyTapMethod(sender: UITapGestureRecognizer) {
        self.view.endEditing(true)
    }

}

 

이와같이, Observer를 추가 & 삭제하는 작업이 번거롭게 반복 될 뿐더러

selector함수 또한 정의를 따로 해주어야 합니다.

 

 

 

 

 

 

 

 

 

 

RxSwift를 사용한 KeyboardNotification 다루기

 

RxSwift를 이용하면 Notificationcenter을 다룰 때, 이런 번거로운 작업을 덜어낼 수 있습니다.

아래의 코드는 RxSwift를 사용하여 KeyboardNotification을 다룬 내용입니다.

또한, 여러개의 TextField가 있을 때 특정 TextField를 선택했을 때 동작하는 코드입니다. 

(해당 코드는 MVVM패턴으로 작성 되었습니다.)

 

 

SignUpViewController

class SignUpViewController: BaseViewController{

    private let viewModel = SignUpViewModel()



    @IBOutlet weak var rePasswordTextField: UITextField!
    @IBOutlet weak var compleBtn: UIButton!

    override func viewDidLoad() {
        self.bindUI()
    }

    func bindUI(){
            /// 해당 ViewController에는 현재, 3개의 TextField가 존재하고
            /// rePasswordTextField는 ScrollView의 가장 최하단에 위치한 TextField이다.

            //MARK: rePasswordTextField 터치하면 높이 변경
            rePasswordTextField.rx.beginEditing
                .subscribe(onNext: { [weak self] in
                    guard let self = self else {return}
                    self.viewModel.moveKeyBoardHeight(scrollView: self.scrollView, BaseUIBtn: self.compleBtn)
                })
                .disposed(by: disposeBag)
    }
}

 

 

SignUpViewModel

	final class SignUpViewModel {
    let login = AuthService.shared
    let disposeBag = DisposeBag()
    
    let keyboardHeight = BehaviorRelay<CGFloat>(value: 0)
    
    
    func moveKeyBoardHeight(scrollView : UIScrollView, BaseUIBtn : UIButton){
        let keyboardWillShow = NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
        let keyboardWillHide = NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)

        Observable.merge(keyboardWillShow, keyboardWillHide)
        
            // take를 설정하지 않으면, rePasswordTextField를 선택 한 순간부터 계속 stream이 연결되어
            // 다른 textField를 선택 했을 때 도, scrollView의 이동이 발생함.
            // 따라서, take(2)를 선택하여 rePasswordTextField를 열고, 닫는 2번의 동작을 마치면 stream이 알아서 끊어지게 만듦.
            // (하지만, 해당 코드는 임시방편이기 때문에 유저가 알아서 개선하길 바람)
            .take(2)
            .subscribe(onNext: { [weak self] notification in
                guard let self = self else {return}
                guard let userInfo = notification.userInfo,
                      let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {return}

                // Notification의 이름에 따라 Keyboard의 높이를 주거나 0의 높이를 부여함
                let keyboardHeight = notification.name == UIResponder.keyboardWillShowNotification ? keyboardFrame.height : 0
                self.keyboardHeight.accept(keyboardHeight)
                
                self.keyboardHeight
                // 저장된 keyboardHeight의 값을 이용하여 ScrollView의 bottomInset값 설정
                    .subscribe(onNext: { keyboardHeight in
                        let contentInset = scrollView.contentInset
                        let bottomInset = keyboardHeight > 0 ? keyboardHeight : 0
                        scrollView.contentInset.bottom = bottomInset
                        scrollView.verticalScrollIndicatorInsets.bottom = bottomInset
                        let offsetY = BaseUIBtn.frame.maxY - (scrollView.bounds.height - bottomInset - 10)
                        let contentOffset = CGPoint(x: 0, y: max(offsetY, 0))
                        scrollView.setContentOffset(contentOffset, animated: true)
                    })
                    .disposed(by: self.disposeBag)
            })
            .disposed(by: disposeBag)
    }
    
    
}

 

728x90
반응형
댓글
반응형
최근에 올라온 글
Total
Today
Yesterday