[iOS]使用Swift解決鍵盤擋住輸入框問題
前言
iOS開發會遇到的問題中,鍵盤擋到輸入框這件事一定包括在內
網路上也提供了很多解決的方案
我自己評估了方便性、實作難易度、呈現效果後
提供給大家一個我自己比較喜歡的一種方式
範例(in Swift 5)
需要將想監聽的輸入框元件的 delegate 設定到本 ViewController
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
/* 暫存輸入框元件 */
var currentTextField: UITextField?
/* 暫存 View 的範圍 */
var rect: CGRect?
func textFieldDidBeginEditing(_ textField: UITextField) {
/* 開始輸入時,將輸入框實體儲存 */
currentTextField = textField
}
override func viewDidLoad() {
super.viewDidLoad()
/* 監聽 鍵盤顯示/隱藏 事件 */
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil)
/* 將 View 原始範圍儲存 */
rect = view.bounds
}
/* 這個地方寫法有問題,請看文章下方補充
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
/* 移除監聽 */
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillShowNotification,
object: nil
)
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillHideNotification,
object: nil
)
}
*/
@objc func keyboardWillShow(note: NSNotification) {
if currentTextField == nil {
return
}
let userInfo = note.userInfo!
/* 取得鍵盤尺寸 */
let keyboard = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
/* 取得焦點輸入框的位置 */
let origin = (currentTextField?.frame.origin)!
/* 取得焦點輸入框的高度 */
let height = (currentTextField?.frame.size.height)!
/* 計算輸入框最底部Y座標,原Y座標為上方位置,需要加上高度 */
let targetY = origin.y + height
/* 計算扣除鍵盤高度後的可視高度 */
let visibleRectWithoutKeyboard = self.view.bounds.size.height - keyboard.height
/* 如果輸入框Y座標在可視高度外,表示鍵盤已擋住輸入框 */
if targetY >= visibleRectWithoutKeyboard {
var rect = self.rect!
/* 計算上移距離,若想要鍵盤貼齊輸入框底部,可將 + 5 部分移除 */
rect.origin.y -= (targetY - visibleRectWithoutKeyboard) + 5
UIView.animate(
withDuration: duration,
animations: { () -> Void in
self.view.frame = rect
}
)
}
}
@objc func keyboardWillHide(note: NSNotification) {
/* 鍵盤隱藏時將畫面下移回原樣 */
let keyboardAnimationDetail = note.userInfo as! [String: AnyObject]
let duration = TimeInterval(truncating: keyboardAnimationDetail[UIResponder.keyboardAnimationDurationUserInfoKey]! as! NSNumber)
UIView.animate(
withDuration: duration,
animations: { () -> Void in
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: -self.view.frame.origin.y)
}
)
}
}
補充
社團 iOS @ Taipei 內的開發者們提供一些建議與資訊
建議1
在 iOS 9 之後可以選擇不手動移除觀察者,原因在於會對觀察者採用 weak reference
與 unsafe unretained 的差異為若對象消滅了,unsafe unretained 並不會將內容改為nil
故有機會造成系統崩潰,詳細內容請參考官方文件
如此一來上面範例中的 viewDidDisappear 函式將可移除不使用
建議2
如需要手動移除,建議將註冊觀察者放至於 viewWillAppear 函式
移除則放至於 viewWillDisappear 函式內
原因在於 viewDidLoad 只在第一次加載 View 後調用
而 viewDidDisappear 則是 View 發生覆蓋、隱藏、消滅時調用,包括跳轉到其他頁面
這樣會造成跳轉時發生移除觀察者的行為,返回原畫面並不會重新註冊觀察者(不會再次執行 viewDidLoad)
效果
切換輸入框會重新計算一次高度
若這邊教學有幫助到你的話~請多多分享轉發出去給更多的人知道
謝謝大家的觀看