当前位置: > > > Swift - 键盘出现后自动改变页面布局,防止下方元素被键盘遮挡

Swift - 键盘出现后自动改变页面布局,防止下方元素被键盘遮挡

(本文代码已升级至Swift4)

通常点击页面上的文本输入框时,屏幕下方就会弹出虚拟键盘。这时页面布局是不会发生变化的,那么键盘便会遮挡住屏幕的下半部分。但如果我们的输入框刚好在底部,那么键盘出现后就完全看不到输入框了。

比如我在页面下方添加了一个 toolBartoolBar 里面有输入框。同时给 toolBar 添加个约束,使其一直在页面底部。

可以看到虚拟键盘上移后,把 toolBar 给遮挡住了。
             

解决办法:
监听键盘通知(UIKeyboardWillChangeFrame),当键盘出现或者隐藏时我们动态改变 View 的高度,位置等,实现自适应布局。
本例便是改变 toolBar 的下约束,就算键盘出现,toolBar 也能在键盘的上方。
(注:键盘出现、隐藏的过程中,toolBar 上下移动是跟随着键盘有动画效果的)

代码如下: 
import UIKit

class ViewController: UIViewController {
    
    //toolBar的下约束
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default.addObserver(self,
                            selector: #selector(ViewController.keyboardWillChange(_:)),
                            name: .UIKeyboardWillChangeFrame, object: nil)
    }
    
    // 键盘改变
    @objc func keyboardWillChange(_ notification: Notification) {
        if let userInfo = notification.userInfo,
            let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue,
            let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
            let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt {
            
            let frame = value.cgRectValue
            let intersection = frame.intersection(self.view.frame)
            
            //self.view.setNeedsLayout()
            //改变下约束
            self.bottomConstraint.constant = -intersection.height
            
            UIView.animate(withDuration: duration, delay: 0.0,
                           options: UIViewAnimationOptions(rawValue: curve), animations: {
                            
                            self.view.layoutIfNeeded()
            }, completion: nil)
        }
    }
    
    @IBAction func sendMessage(_ sender: AnyObject) {
        //关闭键盘
        textField.resignFirstResponder()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
效果图如下:

源码下载:hangge_911.zip
评论7
  • 7楼
    2017-12-21 08:49
    小豪

    感谢站长,学习了

    站长回复

    不客气,欢迎常来看看,我会持续更新下去的。

  • 6楼
    2017-11-25 16:12
    小豪

    站長,有swift4版本嗎?

    站长回复

    代码已更新,你可以再看下。

  • 5楼
    2017-11-25 09:34
    小豪

    站長,現在是不是沒有bottomConstraint,現在View不是自帶Safe Area,還能用bottomConstraint嗎?

    站长回复

    bottomConstraint 还是有的,你大概是把它和 bottomLayoutGuide 给混淆了。iOS11 中引入的 Safe Area 是取代原先的 topLayoutGuide 和 bottomLayoutGuide。

  • 4楼
    2016-03-15 11:05
    moon

    改成self.view.layoutIfNeeded()以后还是不行?

    站长回复

    那我也不清楚是什么问题了,我这边又测试了下是好的。

  • 3楼
    2016-03-09 12:47
    王荣

    你好,我的还是没有动画效果,我用的storyboard是不是有关系? 下面是我的代码
    func keyboardWillChange(notification:NSNotification){

    if let userInfo = notification.userInfo,
    value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue,
    duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
    curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt {

    let frame = value.CGRectValue()
    let intersection = CGRectIntersection(frame, self.view.frame)
    //改变下约束
    self.stackViewBot.constant = CGRectGetHeight(intersection)

    UIView.animateWithDuration(duration, delay: 0.0,
    options: UIViewAnimationOptions(rawValue: curve), animations: {
    _ in
    self.view.setNeedsLayout()
    }, completion: nil)
    }
    }

    站长回复

    我把self.view.setNeedsLayout()改成self.view.layoutIfNeeded()了,你试试看

  • 2楼
    2016-03-06 10:19
    王荣

    你好为什么没有动画效果呢?

    站长回复

    我把代码修改了下,你再试试看。

  • 1楼
    2016-01-22 10:06
    Mads

    如何直接通过代码的形式给输入框添加约束呢?

    站长回复

    用代码设置约束也是可以的,就是麻烦些。可以参考我的这篇文章:http://www.hangge.com/blog/cache/detail_746.html