본문 바로가기

UIKIT/Keyboard

🌈 텍스트 필드가 키보드가 가린다면?

키보드가 나타날 때, reviewTitleTextField가 가려지는 경우
뷰 전체를 키보드 높이만큼 올렸다가, 키보드가 사라지면 원래 위치로 복귀하도록 처리해야 합니다.

 

✈️ 전체 코드

class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var textField: UITextField!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setupKeyboardNotifications()
        
    }
    
    private func setupKeyboardNotifications() {
        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 private func keyboardWillShow(_ notification: Notification) {
        guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
        
        let keyboardHeight = keyboardFrame.cgRectValue.height
        let textFieldBottom = textField.frame.origin.y + textField.frame.height
        let keyboardTop = UIScreen.main.bounds.height - keyboardHeight
        
        if textFieldBottom > keyboardTop {
            let offset = textFieldBottom - keyboardTop + 20 // 키보드 위로 20pt 여유
            UIView.animate(withDuration: 0.3) {
                self.view.frame.origin.y -= offset
            }
        }
    }
    
    @objc private func keyboardWillHide(_ notification: Notification) {
        UIView.animate(withDuration: 0.3) {
            self.view.frame.origin.y = 0 // 원래 위치로 복귀
        }
    }
}

 

🚜 setupKeyboardNotifications() 구현

private func setupKeyboardNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

 

설명

  • 키보드가 나타날 때 → keyboardWillShow(_:) 호출
  • 키보드가 사라질 때 → keyboardWillHide(_:) 호출

 

 

🚋 키보드가 나타날 때 뷰를 올리기

@objc private func keyboardWillShow(_ notification: Notification) {
    guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

    let keyboardHeight = keyboardFrame.cgRectValue.height
    let textFieldBottom = textField.frame.origin.y + textField.frame.height
    let keyboardTop = UIScreen.main.bounds.height - keyboardHeight

    if textFieldBottom > keyboardTop {
        let offset = textFieldBottom - keyboardTop + 20 // 키보드 위로 20pt 여유
        UIView.animate(withDuration: 0.3) {
            self.view.frame.origin.y -= offset
        }
    }
}


✅ 설명

  • 키보드의 높이를 구함
  • reviewTitleTextField의 바닥 위치를 계산
  • reviewTitleTextField가 키보드에 가려지는지 확인
  • 가려진다면, contentView를 키보드 높이만큼 위로 올림

 

🛵 키보드가 사라질 때 뷰 원위치로 복귀

@objc private func keyboardWillHide(_ notification: Notification) {
    UIView.animate(withDuration: 0.3) {
        self.contentView.frame.origin.y = 0 // 원래 위치로 복귀
    }
}

설명

  • 키보드가 사라지면 contentView를 원래 위치로 복귀
  • 애니메이션 효과 적용하여 부드럽게 이동

 

 


 

🚗 문제 발생 

moveViewForKeyboard(up:) 메서드에서 view.frame.origin.y를 직접 조정하는 방식의 문제

  • view.frame.origin.y를 직접 변경하면 애니메이션 충돌 가능성이 있음.
  • 키보드가 여러 번 올라오거나 사라질 때 뷰가 여러 번 이동할 가능성이 있음.
class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var textField: UITextField!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setupKeyboardNotifications()
        textField.backgroundColor = .systemMint
        textView.backgroundColor = .systemCyan
        
        view.addGestureRecognizer(UIGestureRecognizer(target: self, action: #selector(didTapView)))
        
        //textField.delegate = self
    }
    
    private func setupKeyboardNotifications() {
        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 private func didTapView() {
        view.endEditing(true)
    }
    
    @objc private func keyboardWillShow(_ notification: Notification) {
        moveViewForKeyboard(up: true)
    }
    
    @objc private func keyboardWillHide(_ notification: Notification) {
        moveViewForKeyboard(up: false)
    }
    
    private func moveViewForKeyboard(up: Bool) {
        let movement: CGFloat = up ? -150 : 150 // ✅ 키보드 크기에 맞춰 조정
        UIView.animate(withDuration: 0.3, animations: {
            self.view.frame.origin.y += movement
        })
    }
}

 

 


 

✅ 올바른 해결 방법

👉 키보드 크기(keyboardFrame.height)를 정확하게 계산하여 뷰를 이동하도록 수정
👉 뷰의 현재 위치를 저장하고, 여러 번 이동하는 것을 방지
👉 UITextFieldDelegate에서 직접 뷰를 이동하지 않고, 키보드 알림(Notification)만 사용

 

class ViewController: UIViewController {
    
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var textField: UITextField!
    
    // 🚜 원래 뷰의 Y의 위치 저장
    private var originalViewY: CGFloat = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupKeyboardNotifications()
        textField.backgroundColor = .systemMint
        textView.backgroundColor = .systemCyan
        
        view.addGestureRecognizer(UIGestureRecognizer(target: self, action: #selector(didTapView)))
        
        // 🚜 현재 위치 저장
        originalViewY = view.frame.origin.y
    }
    
    private func setupKeyboardNotifications() {
        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 private func didTapView() {
        view.endEditing(true)
    }
    
    @objc private func keyboardWillShow(_ notification: Notification) {
        guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
        
        let keyboardHeight = keyboardFrame.cgRectValue.height
        let textFieldBottom = textField.frame.origin.y + textField.frame.height
        let keyboardTop = UIScreen.main.bounds.height - keyboardHeight
        
        if textFieldBottom > keyboardTop {
            let offset = textFieldBottom - keyboardTop + 20
            UIView.animate(withDuration: 0.3) {
                self.view.frame.origin.y = self.originalViewY - offset
            }
        }
    }
    
    @objc private func keyboardWillHide(_ notification: Notification) {
        UIView.animate(withDuration: 0.3) {
            self.view.frame.origin.y = self.originalViewY // ✅ 원래 위치로 복귀
        }
    }

}

 


✅ 텍스트 필드와 텍스트 뷰 모두 키보드에 가리지 않도록 처리하는 방법

📌 핵심 로직

  1. 현재 어떤 입력창(textField or textView)이 활성화되었는지 확인
  2. 해당 입력창이 키보드에 가려질 경우 자동으로 이동
  3. 키보드가 사라지면 원래 위치로 복귀
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var textField: UITextField!
    
    private var activeTextInput: UIView? // ✅ 현재 활성화된 입력창 (UITextField or UITextView)
    private var originalViewY: CGFloat = 0 // ✅ 원래 뷰의 Y 위치 저장

    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupKeyboardNotifications()
        textField.backgroundColor = .systemMint
        textView.backgroundColor = .systemCyan
        
        originalViewY = view.frame.origin.y // ✅ 원래 위치 저장
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapView))
        view.addGestureRecognizer(tapGesture)

        // ✅ 델리게이트 설정
        textField.delegate = self
        textView.delegate = self
    }
    
    private func setupKeyboardNotifications() {
        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 private func didTapView() {
        view.endEditing(true) // ✅ 키보드 내리기
    }
    
    // 🚕 핵심 코드 부분
    @objc private func keyboardWillShow(_ notification: Notification) {
        guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

        let keyboardHeight = keyboardFrame.cgRectValue.height
        guard let activeInput = activeTextInput else { return } // ✅ 현재 활성화된 입력창 확인

        let inputBottom = activeInput.frame.origin.y + activeInput.frame.height
        let keyboardTop = UIScreen.main.bounds.height - keyboardHeight

        // 🚕 핵심 코드 부분
        if inputBottom > keyboardTop {
            let offset = inputBottom - keyboardTop + 20 // ✅ 키보드 위로 20pt 여유
            UIView.animate(withDuration: 0.3) {
                self.view.frame.origin.y = self.originalViewY - offset
            }
        }
    }
    
    @objc private func keyboardWillHide(_ notification: Notification) {
        UIView.animate(withDuration: 0.3) {
            self.view.frame.origin.y = self.originalViewY // ✅ 원래 위치로 복귀
        }
    }
}

// ✅ UITextFieldDelegate
extension ViewController: UITextFieldDelegate {
    func textFieldDidBeginEditing(_ textField: UITextField) {
        activeTextInput = textField // ✅ 현재 활성화된 입력창 저장
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        activeTextInput = nil
    }
}

// ✅ UITextViewDelegate
extension ViewController: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        activeTextInput = textView // ✅ 현재 활성화된 입력창 저장
    }
    
    func textViewDidEndEditing(_ textView: UITextView) {
        activeTextInput = nil
    }
}

 

400