Day 68 - UITextView에서 키보드 높이 조절

sun02·2021년 12월 6일
0

100 days of Swift 

목록 보기
36/40

UITextField와 다르게 UITextView에서는 여러 줄의 텍스트를 작성할 수 있습니다.
그렇기에 텍스트를 많이 작성한 경우, 작성한 텍스트가 키보드에 가려 보이지 않는 이슈가 발생합니다.

NotificationCenter

상태가 변화할 때마다 그 변화를 알려주는 notificationCenter 클래스

화면 뒤에서 iOS는 무언가가 발생할 때마다 계속해서 notification을 보냅니다.

따라서 특정 notification에 자신을 observer로 추가하고,
notification이 발생할 때마다 호출 될 method를 추가하여
유용한 정보를 전달받을 수 있게 합니다.

키보드에 대한 동작 중 고려해야하는 notifications

  • keyboardWillHideNotification : 키보드 숨기기가 완료될 때 전송

  • keyboardWillChangeFrameNotification : 키보드 보여주기, 숨기기, 방향, QuickType 등 키보드 상태 변경이 발생할 때 전송

    • keyboardWillChangeFrameNotification에 키보드 숨기기가 있기에 keyboardWillHideNotification이 불필요해보일 수 있지만 종종 keyboardWillChangeFrameNotification이 포착하지 못하는 경우도 있기 때문에 확실히해두기 위해 넣어줍니다.

- 스스로를 observer로 등록하기

1. Notification center 참조

let notificationCenter = NotificationCenter.default

2. addObserver() 메서드를 사용

notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)

addObserver()의 파라미터

  • self : notification을 받게될 object
  • selector : 호출 될 메서드
  • name: 우리가 받을 notification
  • object: 관찰 할 object
    • object: nil => notification을 보내는 주체는 신경쓰지 않음

- 호출 될 method(adjustForKeyboard()) 작성하기

1. adjustForkeyboard()의 파라미터 타입 Notification

@objc func adjustforKeybard(notification: Notification) {

}

  • Notification은 userInfo라는 알림 이름과 알림 관련 정보가 포함된 Dictionary를 가짐

2. cgRectValue 가져오기

@objc func adjustForKeyboard(notification: Notification) {
        
        guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
        let keyboardScreenEndFrame = keyboardValue.**cgRectValue**
        
}
  • userInfo 딕셔너리는 keyboard의 동작에 관련된 UIResponder.keyboardFrameEndUserInfoKey라는 키를 가지고 이 키는 동작이 끝난 후 키보드의 frame값을 제공합니다.
      • 이 frame 값의 타입은 NSValue이고 곧 CGRect(CGPoint + CGSize)타입 입니다.
      • Objective - C에서 배열과 딕셔너리는 CGRect를 가질 수 없기 때문에 NSValue라는 특수 클래스로 CGRect를 가지고, cgRectValue를 사용해서 값을 읽을 수 있습니다.

3. view의 좌표로 변환하기

    @objc func adjustForKeyboard(notification: Notification) {
        
        guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
        let keyboardScreenEndFrame = keyboardValue.cgRectValue
        
        
        let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
        
  • 키보드의 frame을 알게 되면, 해당 frame을 view의 좌표로 변환해야합니다.
    • rotation은 frame에 반영되지 않기 때문에, convert()로 변환하지 않는다면 기기의 모드가 landscape인 경우 너비와 높이가 뒤집힐 것이기 때문입니다.

4. contentInset 조정하기


@objc func adjustForKeyboard(notification: Notification) {
        
        guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
        let keyboardScreenEndFrame = keyboardValue.cgRectValue
        let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)

        if notification.name == UIResponder.keyboardWillHideNotification {
            script.contentInset = .zero
        } else {
            script.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height - view.safeAreaInsets.bottom, right: 0)
        }
  • notification이 keyboardWillHideNotification가 아닌 경우 script(textView)의 contentInset의 bottom을 keyboardViewEndFrame의 height - view.safeAreaInsets.bottom만큼 올려줍니다.
    • iphone SE 모델처럼 홈버튼이 있지 않은 모델(최근 나온 기기는 다 없다)인 경우, safeAreaInset.bottom이 자동으로 지정되어 있기 때문에, 이 것을 제외해주어야 textView가 키보드 바로 위까지 보여질 수 있습니다.

5. scrollIndicatorInsets 조정하기


@objc func adjustForKeyboard(notification: Notification) {
        
        guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
        let keyboardScreenEndFrame = keyboardValue.cgRectValue
        let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)

        if notification.name == UIResponder.keyboardWillHideNotification {
            script.contentInset = .zero
        } else {
            script.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height - view.safeAreaInsets.bottom, right: 0)
        }

        script.scrollIndicatorInsets = script.contentInset

        let selectedRange = script.selectedRange
        script.scrollRangeToVisible(selectedRange)
    }
    

0개의 댓글