textFiled 遮住鍵盤問題解決方案 === # 問題 `mainView` 是包含一個 `tableView` 與 `textField` 點擊 `textField` 時鍵盤不要擋住,希望能把 mainview 往上移動到鍵盤的上方 # Method 1 ```swift class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var textfield: UITextField! @IBOutlet weak var titleView: UIView! @IBOutlet weak var mainView: UIView! override func viewDidLoad() { super.viewDidLoad() textfield.delegate = self // 增加點擊背景收起鍵盤 addHideKeyboardWhenTappedAround() NotificationCenter.default.addObserver(self, selector: #selector(keyboardShown), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardHidden), name: UIResponder.keyboardWillHideNotification, object: nil) } override func viewWillDisappear(_ animated: Bool) { { NotificationCenter.default.removeObserver(self) } private func addHideKeyboardWhenTappedAround() { let tapAround: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) // tapAround.cancelsTouchesInView = false view.addGestureRecognizer(tapAround) } @objc func dismissKeyboard() { view.endEditing(true) } @objc func keyboardShown(notification: Notification) { guard let info = notification.userInfo, let keyboardFrame = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } // 鍵盤高度 let keyboardHeight = keyboardFrame.height // textfield 在 self.view 座標系的位置 let editingTextFieldFrame = textfield.convert(textfield.bounds, to: self.view) // textfield 的底部 Y 座標 let editingTextFieldMaxY = editingTextFieldFrame.maxY // 畫面高度 - 鍵盤高度 = 鍵盤上緣 let keyboardMinY = self.view.frame.height - keyboardHeight // textfield 與鍵盤的間距,如果小於 0 代表被擋住 let overlap = editingTextFieldMaxY - keyboardMinY + 20 // +20 buffer if overlap > 0 { UIView.animate(withDuration: 0.25) { self.mainView.transform = CGAffineTransform(translationX: 0, y: -overlap) } } } @objc func keyboardHidden(notification: Notification) { UIView.animate(withDuration: 0.25) { self.mainView.transform = .identity } } } ``` 使用 `transform` 和 `CGRect` 在 iOS UI 裡的差異,核心在於影響的層次不同 | 特性 | `CGRect` (`frame`) | `transform` | | ---------- | ------------------------------ | ------------------------- | | 作用 | 直接改變 View 的位置與大小 | 在顯示層做仿射變換(Affine Transform),不動原本的 frame 定義 | | AutoLayout | 容易衝突(因為 frame 被 constraint 控制) | 不影響 constraint,比較安全 | | 動畫用途 | 一般較少用,需重新 layout | 非常常用(移動 translation、縮放 scale、旋轉 rotation) | | 適合場景 | 需要永久改變 View 的位置/大小 | 暫時調整或動畫效果 | ```swift= view.frame = CGRect(x: 0, y: 100, width: 200, height: 50) // 把 view 移到 y = 100,寬高變 200x50 view.transform = CGAffineTransform(translationX: 0, y: -100) // 向上移動 100 view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5) // 放大 1.5 倍 view.transform = CGAffineTransform(rotationAngle: .pi/4) // 旋轉 45 度 ``` # Method 2 iOS15+ 新推出的 **UIKeyboardLayout**,可以自動解決鍵盤擋住畫面的問題。 需滿足: - 確認要移動的 `UIView`,這邊假設是上述的 `mainView` - 要移動的 `UIView` 的底部必須對齊 1. `UIView` 的 **Layout Guides** 啟用 **Keyboard** 選項 ![截圖 2025-08-29 11.33.13](https://hackmd.io/_uploads/Hkzj1i0Yxg.png) 2. 在 xib 左側就會出現 **Keyboard Layout guide**,直接將 `mainView` 拖曳到 **Keyboard Layout Guide** 設置對齊它的 top。 ![截圖 2025-08-29 11.37.39](https://hackmd.io/_uploads/H1Zslj0Kex.png) 注意:若是要被往上移的 UIView 是子 UIView,則每一層的 super view 都要設置底部對齊 **Keyboard Layout Guide** ```swift= override func viewDidLoad() { super.viewDidLoad() // mainView 底部 = 鍵盤上緣 - 0(或留個間距 8) mainView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor, constant: 0).isActive = true } ``` 這樣做效果就是: - 鍵盤收起時 → keyboardLayoutGuide.topAnchor 會等於 Safe Area Bottom,所以 `mainView` 會貼到底部。 - 鍵盤彈出時 → keyboardLayoutGuide.topAnchor 自動移到鍵盤上緣,`mainView` 會跟著被頂上去。 - 不需要寫 `NotificationCenter` 的監聽,也不用自己算 offset。 ###### tags: `textField`