Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Human interface guide
- uiscrollview
- Protocol
- 리펙토링
- swiftUI
- collectionview
- combine
- HIG
- UICollectionView
- UITextView
- 리팩토링
- Refactoring
- 스위프트
- RxCocoa
- Observable
- MVVM
- SWIFT
- map
- uitableview
- 클린 코드
- ios
- Xcode
- ribs
- 리펙터링
- tableView
- swift documentation
- Clean Code
- clean architecture
- rxswift
- 애니메이션
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. 키보드 처리 - 키보드가 올라갈 때 뷰를 올리는 UI 본문
1. 키보드 처리 - 키보드가 올라갈 때 뷰를 올리는 UI
2. 키보드 처리 - 키보드가 올라갈 때 스크롤 뷰를 올리는 UI
키보드가 올라갈 때 뷰를 올리는 UI
구현 아이디어
- keyboard를 감싸는 투명 UIView, keyboard 바로 위쪽을 감싸는 투명 UIView를 준비
- 투명 UIView는 hitTest를 사용하여 pass through하게 구현 (PassThroughView 구현은 이전 포스팅 글 참고)
- 키보드 바로 위쪽을 감싸는 투명 UIView위에 UITextView, UIButton을 두어서 구현
구현
- 사용한 라이브러리
pod 'SnapKit'
pod 'Then'
pod 'RxSwift'
pod 'RxCocoa'
pod 'RxGesture'
- 상속보다는 유지보수에 용이한 프로토콜 형태인 KeyboardWrappable 구현
- 해당 프로토콜을 채택하는 UIViewController에서 사용할 수 있도록 구현
import UIKit
import SnapKit
import RxSwift
protocol KeyboardWrapperable {
var keyboardWrapperView: PassThroughView { get }
var keyboardSafeAreaView: PassThroughView { get }
var disposeBag: DisposeBag { get }
func setupKeybaordWrapper()
}
- 사용하는쪽에서 setupKeyboardWrapper()를 한번이라도 호출해야하므로, 이를 개발자에게 명시해주기 위해서 flag를 하나 구현
private struct AssociatedKeys {
static var isEnabled = "isEnabled"
}
extension KeyboardWrapperable where Self: UIViewController {
private var isEnabled: Bool {
get {
(objc_getAssociatedObject(self, &AssociatedKeys.isEnabled) as? Bool) ?? false
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.isEnabled, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
- extension에 이어서 구현하면, setupKeyboardWrapper()에서 isEnabled 플래그를 변경한 후 필요한 레이아웃과 키보드의 높이가 변경될때마다 autolayout으로 키보드 wrapperView를 조정하는 코드 호출
func setupKeybaordWrapper() {
guard !isEnabled else { return }
isEnabled.toggle()
setupLayout()
observeKeyboardHeight()
}
private func setupLayout() {
// TODO
}
private func observeKeyboardHeight() {
// TODO
}
- setupLayout() 구현
- wrapperView를 view에 넣어주고 autolayout 설정
view.addSubview(keyboardWrapperView)
view.addSubview(keyboardSafeAreaView)
keyboardWrapperView.snp.makeConstraints {
$0.leading.trailing.bottom.equalToSuperview()
$0.height.equalTo(0).priority(.high)
}
keyboardSafeAreaView.snp.makeConstraints {
$0.top.leading.trailing.equalToSuperview()
$0.bottom.equalTo(keyboardWrapperView.snp.top)
}
- observeKeyboardHeight() 구현
- 키보드의 높이가 변경될때마다 wrapperView의 height를 변경
- 아래 코드에서 transition 애니메이션을 넣는데, options에 init(rawValue: 458752)값을 넣는데 이는 (.transitionCrossDissolve, .curveEaseInOut) 두개의 애니메이션을 OR로 넣은 것이므로 키보드의 애니메이션과 동일한 옵션
private func observeKeyboardHeight() {
Observable<(Bool, Notification)>
.merge(
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
.map { notification in (true, notification) },
NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)
.map { notification in (false, notification) }
)
.bind(with: self) { ss, tuple in
let (isKeyboardUp, notification) = tuple
let uesrInfo = notification.userInfo
guard let endFrame = uesrInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
let endFrameMinY = endFrame.origin.y
let shownKeyboardHeight = isKeyboardUp ? endFrame.height : 0
ss.keyboardWrapperView.snp.updateConstraints {
$0.height.equalTo(shownKeyboardHeight).priority(.high)
}
UIView.transition(
with: ss.keyboardWrapperView,
duration: 0.25,
options: .init(rawValue: 458752),
animations: ss.view.layoutIfNeeded
)
}
.disposed(by: disposeBag)
}
(KeyboardWrappable 모두 구현 완료)
사용하는쪽
- ViewController에서 KeyboardWrappable을 채택하고 setupKeyboardWrapper()를 호출
- keyboardSafeAreaView에다가 원하는 뷰를 삽입한 후 autolayout 설정만 하면 완료
class ViewController: UIViewController, KeyboardWrapperable {
var keyboardWrapperView = PassThroughView()
var keyboardSafeAreaView = PassThroughView()
...
override func viewDidLoad() {
super.viewDidLoad()
setupKeybaordWrapper()
setupUI()
}
private func setupUI() {
keyboardSafeAreaView.addSubview(textView)
keyboardSafeAreaView.addSubview(button)
textView.snp.makeConstraints {
$0.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(30)
$0.leading.trailing.equalToSuperview().inset(30)
$0.height.equalTo(Metric.textViewHeight).priority(.medium)
$0.bottom.lessThanOrEqualTo(button.snp.top).offset(-Metric.spacing)
}
button.snp.makeConstraints {
$0.leading.trailing.bottom.equalToSuperview()
$0.height.equalTo(80)
}
}
}
(완료)
* 전체 코드
https://github.com/JK0369/ExKeyboardSafeAreaView
'UI 컴포넌트 (swift)' 카테고리의 다른 글
Comments