티스토리 뷰
🍎/iOS
[iOS/UIScrollView] RxSwift를 사용하여, KeyboardNotification 다루기 ( + textField 선택 시 Keyboard 높이만큼 ScrollView이동 )
ir.__.si 2023. 2. 19. 04:52728x90
반응형
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
반응형
'🍎 > iOS' 카테고리의 다른 글
WWDC 2016 - Go Live with ReplayKit (0) | 2025.01.12 |
---|---|
[Xcode] Xcode 프로젝트 구조 분석 도구 : Codeface (0) | 2023.04.05 |
[iOS] 의존성 주입(Dependency Injection) 개념과 예제 ( feat. Clean Architecture + MVVM) (0) | 2023.03.31 |
[iOS/WKWebView] WebView에서 스와이프 제스쳐를 이용하여 뒤로가기 설정하기 (0) | 2023.03.06 |
[iOS] Xcode의 info.plist 알아보기 (0) | 2022.11.24 |
댓글