티스토리 뷰

앱을 만들다 보면 

사용자가 입력을 위해 키보드를 올리면 

그 키보드가 UI를 가려 엉망이 되는 경우가 생각보다 많습니다.....

 

예를 들면 아래처럼 키보드가 올라오는 바람에 입력창이 안보이는 경우가 있겠죠? 

이렇게 되면 입력한 것도 안보이겠죠?

 

그래서 이번엔 저 키보드가 올라오는 이벤트를 감지하고 

그에 따른 대책(?)을 세워보도록 하겠습니다.  

 

저의 경우에는 보통 저렇게 키보드가 올라오는 화면은 될 수 있으면

ScrollView 를 자주 사용하긴 해요. (그래야 사용자가 입력하다 자유롭게 스크롤 할 수 있으니까요)

-> 이 경우는 기본 설명이 끝난후 예시로 이야기할께요. 

 

그래서 아래 순서대로 정리해볼께요.

1. 키보드 이벤트 잡기 (올라옴 / 닫힘) + 여백 클릭시 키보드 닫기 

2. 키보드 사이즈 구하기 & 대응하기 (+ SafeArea)

3. 예시 (ScrollView 섞어서 보여드릴께요.)

 

일단은 키보드가 올라오는 이벤트부터 잡아봅시다.

 

1. 키보드 이벤트 잡기 (올라옴 / 닫힘) + 여백 클릭시 키보드 닫기 

* 자동으로 키보드가 닫히는게 아니기 때문에 일단 여백을 클릭하면 자동으로 닫히게 하겠습니다.

override func viewDidLoad() {
    super.viewDidLoad()
    // 여백시 키보드 닫기
    let closeKeyboard = UITapGestureRecognizer(target: self, action: #selector(closeKeyAction(tapGestureRecognizer:)))
    self.view.isUserInteractionEnabled = true
    self.view.addGestureRecognizer(closeKeyboard)
}
@objc func closeKeyAction(tapGestureRecognizer :UITapGestureRecognizer){
     // 키보드 닫기
    self.view.endEditing(true)
}

- 이제 아래 함수를 추가하시면 키보드 이벤트를 감지할 수 있어요.

override func viewDidLoad() {// 기존 함수에 아래 한줄을 추가해주세요.
    super.viewDidLoad()
    ...
    self.setKeboardRecognizer()
    ...
}
//MARK: for Keyboard
func setKeboardRecognizer() {
    //set Keyboard Action
    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 func keyboardWillShow(_ sender :Notification){
	print("keyboard open")
}
//키보드 닫힘
@objc func keyboardWillHide(_ sender :Notification){
    print("keyboard close")
}

 

2. 키보드 사이즈 구하기 & 대응하기 (+ SafeArea)

- 위 함수에서 이제 키보드의 높이를 바로 구할 수 있어요.

- 키보드가 올라왔을때 해당 높이를 구하는 방식입니다. 

@objc func keyboardWillShow(_ sender :Notification){
	print("keyboard open")
    // 아래 부분을 추가해주세요.
    if let keyboardFrame: NSValue = sender.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
        let keybaordRectangle = keyboardFrame.cgRectValue // 키보드 전체 사이즈
        let keyboardHeight = keybaordRectangle.height
        print("keyboardHeight  : " + keyboardHeight.description)
     }
}

- 이제 레이아웃을 그에 맞게 대응해야겠죠? 

- SafeArea의 영역의 사이즈도 같이 구해야해야 정확한 계산이 가능하므로 먼저 구하는 함수를 만들어줄께요.

// true는 위쪽 false는 아래쪽 SafeArea 의 높이를 구하는 함수에요.
func getSafeAreaHeight(isTop: Bool) -> CGFloat { 
    let topSafeArea: CGFloat
    let bottomSafeArea: CGFloat
    if #available(iOS 11.0, *) {
        topSafeArea = view.safeAreaInsets.top
        bottomSafeArea = view.safeAreaInsets.bottom
    } else {
        topSafeArea = topLayoutGuide.length
        bottomSafeArea = bottomLayoutGuide.length
    }
    return isTop ? topSafeArea:bottomSafeArea
}

- SafeArea의 하단 높이가 포함 되어있는 키보드 높이이기 때문에 설정할때 주의해야해요.

아래 맨 하단 영역이 SafeArea 에요.

 

- 저는 이렇게 구조를 만들었어요. 

@objc func keyboardWillShow(_ sender :Notification){
    print("keyboard open")
    if let keyboardFrame: NSValue = sender.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
        let keybaordRectangle = keyboardFrame.cgRectValue
        let keyboardHeight = keybaordRectangle.height
        print("keyboardHeight  : " + keyboardHeight.description)
        keyLayoutconstant.constant = keyboardHeight  - getSafeAreaHeight(isTop: false) // 안가리게 높이 조절
     }
}
@objc func keyboardWillHide(_ sender :Notification){
     // 기본값
    keyLayoutconstant.constant = 0 // 키보드 높이 조절 
    print("keyboard close")
}

- 레이아웃은 여러분들이 만드신 부분을 고려해서 넣어주시면 됩니다. (반드시 같을 필요는 없어요.)

- 늘어나야 하는 LayoutConstant 에 적용해주시면 됩니다. 

- 저는 늘어나는 뷰를 색을 다르게해서 눈에 잘 보이도록 만든 겁니다. (여러분의 앱에 맞게 조절해주세요.)

입력창이 위로 올라가는게 보이시나요?

 

3. 예시 (ScrollView 섞어서 보여드릴께요.)

- 이걸 어디에 쓰는 건지 감이 안오시는 분들을 위해 예시를 만들께요.

- 저는 아래 같은 화면을 예시로 만들어볼께요. (핑크색 부분은 스크롤 뷰입니다.)

이런 화면이 필요하면 어떻게 해야할까요.

참고로 아무 작업 안하고 입력창 누르면 아래처럼 보이게 됩니다. 

이러면 입력해도 아무것도 안보이겠죠?

예시인 만큼 전체코드로 올리겠습니다. (스크롤 말고는 바뀐 점이 없어요!)

import Foundation
import UIKit

class FoodViewController: UIViewController {
  
    @IBOutlet weak var totalScrollSv: UIScrollView!
    @IBOutlet weak var bottomScrollLc: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.setKeboardRecognizer()
        // 여백시 키보드 닫기
        let closeKeyboard = UITapGestureRecognizer(target: self, action: #selector(closeKeyAction(tapGestureRecognizer:)))
        self.view.isUserInteractionEnabled = true
        self.view.addGestureRecognizer(closeKeyboard)
    }
    //for Keyboard
    func setKeboardRecognizer() {
        //set Keyboard Action
        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 func keyboardWillShow(_ sender :Notification){
    // 키보드 열기
        if let keyboardFrame: NSValue = sender.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keybaordRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keybaordRectangle.height
   
            bottomScrollLc.constant = keyboardHeight  - getSafeAreaHeight(isTop: false)
            
            // 아래는 키보드가 올라오면 스크롤을 하단으로 내리는 부분입니다.
            self.view.layoutIfNeeded() // 위치조심하세요.
            let bottomOffset = CGPoint(x: 0, y: totalScrollSv.contentSize.height - totalScrollSv.bounds.height + totalScrollSv.contentInset.bottom)
            totalScrollSv.setContentOffset(bottomOffset, animated: true)
         }
    }
    //SafeArea 높이 구하기
    func getSafeAreaHeight(isTop: Bool) -> CGFloat {
        let topSafeArea: CGFloat
        let bottomSafeArea: CGFloat
        if #available(iOS 11.0, *) {
            topSafeArea = view.safeAreaInsets.top
            bottomSafeArea = view.safeAreaInsets.bottom
        } else {
            topSafeArea = topLayoutGuide.length
            bottomSafeArea = bottomLayoutGuide.length
        }
        return isTop ? topSafeArea:bottomSafeArea
    }
    @objc func keyboardWillHide(_ sender :Notification){
    	// 키보드 닫기 
        bottomScrollLc.constant = 0
    }
    // 여백시 키보드 닫기
    @objc func closeKeyAction(tapGestureRecognizer :UITapGestureRecognizer){
        self.view.endEditing(true)
    }

}

이제 이걸 실제로 돌려보면 아래처럼 잘 작동한다는 걸 알 수 있습니다.

 

이제 자연스럽게 움직이는게 보이시나요?

 

아무래도 모바일에서는 사용자의 입력을 위해서라면

키보드가 필요하다보니 알아두면 편리하겠죠?

 

누군가에게는 이 포스트가 도움이 되었으면 좋겠네요.

 

그럼 오늘도 파이팅입니다. 

댓글