관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 키보드 업 동작 시 다른 뷰들도 올라가게끔 하는 방법 (UIView.transition), 키보드 내리기 본문

iOS 응용 (swift)

[iOS - swift] 키보드 업 동작 시 다른 뷰들도 올라가게끔 하는 방법 (UIView.transition), 키보드 내리기

jake-kim 2021. 12. 27. 01:52

키보드에 따라서 별 이미지 뷰 이동

기본 지식 -  UIView.transition

  • transition은 container view 관련된 곳에 애니메이션을 사용할 때 사용
  • 키보드를 올릴 때, 그 위에있는 뷰를 UIView.transition 사용하여 올라가도록 하는데에 사용
  • 주의) UIView.animate vs UIView.transition
    • animate: 프로퍼티 개별적인 애니메이션 (ex- 뷰 확대, 축소, alpha값 변경)
    • transition: 전체적인 스크린에서 동작 (뷰 이동)

출처 - Beginning iPhone Games Development (PJ Cabrera, Peter Bakhirev)

구현 아이디어

  • 뷰 정의
    • 키보드를 감싸는 뷰, keyboardWrapperView 정의
    • 키보드를 감싸는 뷰 위쪽 keyboardSafeAreaView 정의
  • auto layout으로 두 뷰의 레이아웃 정의
  • 키보드의 높이가 변경될 때, keybaordWrapperView의 높이를 updateConstraints
  • 위 뷰들을 BaseViewController에 정의하고 일반 ViewController에서 키보드 업할때 적용할 뷰를 keybaordSafeAreaView에 addSubview하여 사용

BaseViewController에 키보드 관련 뷰 구현

  • 사용할 프레임워크
      pod 'SnapKit'
      pod 'RxKeyboard'​
  • BaseViewController 생성
    import UIKit
    import SnapKit
    import RxSwift
    import RxCocoa
    import RxKeyboard
    
    class BaseViewController: UIViewController {
    
    }​
  • 키보드 관련 뷰 정의
    // MARK: UI
    private let keyboardWrapperView: PassThroughView = {
      let view = PassThroughView()
      view.isUserInteractionEnabled = false
      return view
    }()
    let keyboardSafeAreaView: PassThroughView = {
      let view = PassThroughView()
      return view
    }()​
  • 프로퍼티 정의
    // MARK: Properties
    private let disposeBag = DisposeBag()
    private let keyboardHeight = BehaviorRelay<CGFloat>(value: 0)​
  • viewDidLoad()에서 뷰 레이아웃 정의
    • RxKeyboard로 keyboard의 높이 획득
    • keyboard 높이를 획득하면, keyboardWrapperView의 높이를 updateConstraints를 통해 갱신
    • keyboardWrapperView의 높이가 변하면 그 위에있는 keyboardSafeAreaView도 자동으로 auto layout에 의하여 갱신
      override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addSubview(self.keyboardWrapperView)
        self.view.addSubview(self.keyboardSafeAreaView)
        
        RxKeyboard.instance.visibleHeight
          .asObservable()
          .filter { 0 <= $0 }
          .bind { [weak self] in self?.keyboardHeight.accept($0) }
          .disposed(by: disposeBag)
        
        self.keyboardWrapperView.snp.makeConstraints {
          $0.left.right.bottom.equalToSuperview()
          $0.height.equalTo(0).priority(.high)
        }
        
        self.keyboardSafeAreaView.snp.makeConstraints {
          $0.top.left.right.equalToSuperview()
          $0.bottom.equalTo(self.keyboardWrapperView.snp.top)
        }
        
        self.keyboardHeight
          .withUnretained(self)
          .bind(onNext: { ss, height in
            ss.updateKeyboardHeight(height)
            UIView.transition(
              with: ss.keyboardWrapperView,
              duration: 0.25,
              options: .init(rawValue: 458752),
              animations: ss.view.layoutIfNeeded
            )
          })
          .disposed(by: disposeBag)
      }​
      
      private func updateKeyboardHeight(_ height: CGFloat) {
        self.keyboardWrapperView.snp.updateConstraints {
          $0.height.equalTo(self.keyboardHeight.value).priority(.high)
        }
      }

사용하는 쪽

  • BaseViewController 상속
    import UIKit
    import SnapKit
    
    class ViewController: BaseViewController {
    
    }​
  • keyboard가 위로 올라오면, 같이 올라가게 하고싶은 뷰는 BaseViewController에 정의한 keyboardSafeAreaView.addSubview()로 추가
    private let sampleTextField: UITextField = {
      let textField = UITextField()
      textField.textColor = .black
      textField.borderStyle = .roundedRect
      return textField
    }()
    
    private let myImageView: UIImageView = {
      let view = UIImageView()
      view.image = UIImage(systemName: "star.fill")
      return view
    }()
    
    override func viewDidLoad() {
      super.viewDidLoad()
      
      self.view.addSubview(self.sampleTextField)
      self.keyboardSafeAreaView.addSubview(myImageView) // <- 키보드가 올라가면 같이 따라 올라가게끔 keyboardSafeAreaView에 추가
      
      self.sampleTextField.snp.makeConstraints {
        $0.top.equalToSuperview().inset(56)
        $0.left.right.equalToSuperview().inset(16)
      }
      self.myImageView.snp.makeConstraints {
        $0.centerX.equalToSuperview()
        $0.bottom.equalToSuperview().inset(150) // <- 현재 superView는 키보드를 덮고있는 뷰이므로, 키보드 높이에 종속적인 레이아웃
      }
    }​

키보드 내리기

  • ViewController에서 touchesEnded(_:with:)을 오버라이딩하여 self.view.endEditing(true) 호출
// ViewController.swift
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  super.touchesEnded(touches, with: event)
  self.view.endEditing(true)
}

* 전체 소스 코드: https://github.com/JK0369/ExKeyboard

 

* 참고

- UIView.animate vs UIView.transition: 도서 - Beginning iPhone Games Development (PJ Cabrera, Peter Bakhirev)

Comments