새소식

인기 검색어

iOS/iOS

[iOS] 키보드 자동으로 내리기 및 키보드 위치에 따라 TextField 위치 바꾸기

  • -

기본설정 : 텍스트필드 backgroundColor = .green, 오토레이아웃 leading,trailing, bottom = 10

 

 

 

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

 

위와 같이 TextField를 선택시 키보드가 올라오지만 다른곳을 터치해도 키보드가 내려가지 않는다.

그 이유는 터치이벤트가 발생했을때 해당 터치가 발생한 뷰에 First Responder가 지정되어 becomeFirstResponder가 호출되어 키보드가 올라왔지만 그 이후 First Responder를 해제해 주지 않았기 때문에 키보드가 올라온 상태가 변하지 않는것이다. 

따라서 view를 터치했을때 resignFirstResponder호출로 First Responder를 해제해주면 된다.

 

 

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func dismissKeyboard(sender: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
}

 

UITapGestureRecognizer객체를 생성하여 탭핑이 일어날때 dismissKeyboard함수를 호출하게 만들어준다.

생성한 UITapGestureRecognizer객체를 view에 추가해준다.

dismissKeyboard함수는 textField의 First Responder를 해제해주기 위해 resignFirstResponder를 호출하여 키보드가 내려가게 한다.

 

잘 내려가는것을 확인할수 있다. 하지만 문제는 키보드가 올라왔을때 TextField 가려지는 문제가 발생한다. 

키보드가 올라왔을때 TextField의 위치를 수정해주는 코드를 작성해보자

 

먼저 키보드가 나타날때와 사라질때 발생되는 UIResponder의 Notification이 있는데 아래 네가지순으로 호출된다

UIResponder.keyboardWillShowNotification

UIResponder.keyboardDidShowNotification

UIResponder.keyboardWillHideNotification

UIResponder.keyboardDidHideNotification

이다.

 

각각의 Notification에  호출순서를 알아보기 위해 관찰자를 설정해주면

keyboardWillShow, keyboardDidShow, keyboardWillHide, keyboardDidHide가 순서대로 호출된다.

 

 

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func dismissKeyboard(sender: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
    
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow), name: UIResponder.keyboardWillShowNotification , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self)
        
    }
    
    @objc func keyboardWillShow(notification: NSNotification) {
        print("willshow")
    }
    
    @objc func keyboardDidShow(notification: NSNotification) {
        print("Didshow")
    }
    
    @objc func keyboardWillHide(notification: NSNotification) {
        print("willhide")
    }
    
    @objc func keyboardDidHide(notification: NSNotification) {
        print("Didhide")
    }
}

 

이제 이 willshow와 willhide함수를 사용하여 키보드의 크기를 계산하여 TextField의 constraint를 수정해주는 코드를 추가해주면 된다.

 

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func dismissKeyboard(sender: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
    
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self)
        
    }
    
    @objc func keyboardWillShow(_ notification: NSNotification){
        if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keyboardRectangle.height
            self.textField.frame.origin.y -= keyboardHeight
        }
    }
    
    
    @objc func keyboardWillHide(_ notification: NSNotification){
        if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keyboardRectangle.height
            self.textField.frame.origin.y += keyboardHeight
        }
    }
}

보면 키보드가 올라올때 TextFiel의 y값을 수정해주어 올려주도록 작성했다. 하지만 여기에는 문제가 있는데 키보드를 터치하면 willshow가 두번 호출되는 문제가 발생하여 TextField가 사라진다는 문제가 있다.

 

 

willshow가 두번호출되는 이유는 TextField에 inputaccessoryView 때문인것같다. willshow가 두번호출되는 문제를 방지하기위해서

willshow가 willhide함수가 호출되기 전에 두번호출됬는지 확인하는 변수를 추가해준다.

 

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    var calledWillShow = false
    var calledWillHide = true
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func dismissKeyboard(sender: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
      
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self)
        
    }
    
    @objc func keyboardWillShow(_ notification: NSNotification){
        
        if calledWillShow {
            return
        }
        print("willshow")

        if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keyboardRectangle.height
            self.textField.frame.origin.y -= keyboardHeight
        }
        calledWillShow = true
        calledWillHide = false
    }

    @objc func keyboardWillHide(_ notification: NSNotification){
        if calledWillHide {
            return
        }
        print("willhide")

        if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keyboardRectangle.height
            self.textField.frame.origin.y += keyboardHeight
        }
        calledWillShow = false
        calledWillHide = true
    }
}

calledWillShow 변수와 calledWillHide 변수를 사용해 wilshow가 한번 호출되고 난뒤엔 willhide가 호출되어야 willshow가 호출될수있게 만들어놓았는데 이번엔 키보드를 입력하면 텍스트 필드가 사라져버렸다. 왜 그런가를 확인해보면 위코드에서 willhide함수에서 self.textField.frame.origin.y += keyboardHeight부분을 주석처리한뒤 실행해보면 willshow가 호출되어 TextField를 올려둔뒤 키보드를 입력하면 스토리보드에서 설정한 오토레이아웃으로 인해 자동으로 원래 자리로 돌아온뒤 그곳에서 Height값을 더해주니 뷰의 영역밖으로 나가버렸기 때문에 보이지 않았던것을 확인할수있다.

 

//self.textField.frame.origin.y += keyboardHeight 주석처리

오토레이아웃을 때문에 생긴문제이기때문에 코드를 전체적으로 수정하면 

 

최종

import UIKit

class ViewController: UIViewController {
    
    let textField = UITextField()
    
    var textFieldYValue = CGFloat(0) // 처음 textField의 Y값
    var textFieldConstraint: NSLayoutConstraint?
    
    var calledWillShow = false
    var calledWillHide = true
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        view.addGestureRecognizer(tapGesture)
        view.addSubview(textField)
        textFieldSetting()
    }
    
    func textFieldSetting() {
        textField.backgroundColor = .green
        textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
        textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
        textFieldConstraint = textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10)
        textFieldConstraint?.isActive = true
        textField.translatesAutoresizingMaskIntoConstraints = false
    }

    @objc func dismissKeyboard(sender: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self)
    }
    
    @objc func keyboardWillShow(_ notification: NSNotification){
        if calledWillShow {
            return
        }
        
        if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            if textFieldYValue == 0 { // 만약 willshow가 처음 호출되었다면
                textFieldYValue =  self.textField.frame.origin.y // textField의 y값을 저장
            }

            if textField.frame.origin.y == textFieldYValue { // textField의 위치가 그대로라면
                textField.frame.origin.y -= keyboardFrame.cgRectValue.height // 키보드의 크기만큼 textField를 올린다
                textField.translatesAutoresizingMaskIntoConstraints = true // 오토레이아웃 비활성화
            }
        }
        
        calledWillShow = true
        calledWillHide = false
    }

    @objc func keyboardWillHide(_ notification: NSNotification){
        if calledWillHide {
            return
        }
        
        if textField.frame.origin.y != textFieldYValue { // textField의 위치가 바뀌엇다면
            textField.frame.origin.y = textFieldYValue // textField의 위치를 처음위치로 변경, 이 줄은 주석처리해도 다음줄에 오토레이아웃때문에 잘 돌아감
            textField.translatesAutoresizingMaskIntoConstraints = false // 오토레이아웃 활성화 
        }
        
        calledWillShow = false
        calledWillHide = true
    }
}

 

 

 

'iOS > iOS' 카테고리의 다른 글

[iOS] Xcode Stroyboard 없이 시작하기  (0) 2023.02.02
[iOS] UIStackView  (0) 2023.01.23
[iOS] UITextField  (0) 2023.01.23
[iOS] UIButton  (0) 2023.01.23
[iOS] TableView  (0) 2022.09.20
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.