【问题标题】:Resize the screen when keyboard appears出现键盘时调整屏幕大小
【发布时间】:2015-08-21 21:24:42
【问题描述】:

我正在构建一个聊天应用程序。当键盘出现时,我必须移动一个文本字段。我正在使用以下代码执行此操作:

func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize =  (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            kbHeight = keyboardSize.height
            self.animateTextField(true)
        }
    }
}
func keyboardWillHide(notification: NSNotification) {
    self.animateTextField(false)
}

func animateTextField(up: Bool) {
    var movement = (up ? -kbHeight : kbHeight)

    UIView.animateWithDuration(0.3, animations: {
        self.view.frame = CGRectOffset(self.view.frame, 0, movement)
    })
}

但是当我使用此代码时,第一条消息没有显示。我想我必须调整 tableview 的大小。

以下是键盘出现之前之后的屏幕截图:

我正在使用自动布局。

我该如何解决这个问题?

【问题讨论】:

  • 您是否在情节提要中使用自动布局?
  • 是的,我正在使用自动布局。
  • 哦!...我忘了...对不起。
  • 不要更新frame,更新contentInset

标签: ios swift autolayout


【解决方案1】:

2020 年更新

正确使用约束...

只有一种方法可以在 iOS 中正确处理这种混乱。

  1. 将下面的KUIViewController粘贴到您的项目中,

  2. 创建一个对“内容底部”非常简单的约束

  3. 将该约束拖到bottomConstraintForKeyboard

KUIViewController 将始终自动正确地调整您的内容视图的大小

一切都是全自动

所有 Apple 行为都以标准方式正确处理,例如通过点击关闭等。

你已经 100% 完成了。

那么“你应该调整哪个视图的大小?”

不能使用.view ...

因为...您无法在 iOS 中调整 .view 的大小!!!!!!呵呵!

只需制作一个名为“holder”的 UIView。它位于.view 内。

把你所有的东西都放在“持有人”里面。

对于.view,持有者当然会有四个简单的上/下/左/右约束。

对“持有人”的底部约束确实是bottomConstraintForKeyboard

你已经完成了。

给客户寄账单然后去喝酒。

没有什么可做的了。

class KUIViewController: UIViewController {

    // KBaseVC is the KEYBOARD variant BaseVC. more on this later

    @IBOutlet var bottomConstraintForKeyboard: NSLayoutConstraint!

    @objc func keyboardWillShow(sender: NSNotification) {
        let i = sender.userInfo!
        let s: TimeInterval = (i[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
        let k = (i[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
        bottomConstraintForKeyboard.constant = k
        // Note. that is the correct, actual value. Some prefer to use:
        // bottomConstraintForKeyboard.constant = k - bottomLayoutGuide.length
        UIView.animate(withDuration: s) { self.view.layoutIfNeeded() }
    }

    @objc func keyboardWillHide(sender: NSNotification) {
        let info = sender.userInfo!
        let s: TimeInterval = (info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
        bottomConstraintForKeyboard.constant = 0
        UIView.animate(withDuration: s) { self.view.layoutIfNeeded() }
    }

    @objc func clearKeyboard() {
        view.endEditing(true)
        // (subtle iOS bug/problem in obscure cases: see note below
        // you may prefer to add a short delay here)
    }

    func keyboardNotifications() {
        NotificationCenter.default.addObserver(self,
            selector: #selector(keyboardWillShow),
            name: UIResponder.keyboardWillShowNotification,
            object: nil)
        NotificationCenter.default.addObserver(self,
            selector: #selector(keyboardWillHide),
            name: UIResponder.keyboardWillHideNotification,
            object: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        keyboardNotifications()
        let t = UITapGestureRecognizer(target: self, action: #selector(clearKeyboard))
        view.addGestureRecognizer(t)
        t.cancelsTouchesInView = false
    }
}

简单...

在可能出现键盘的任何地方使用 KUIViewController。

class AddCustomer: KUIViewController, SomeProtocol {

class EnterPost: KUIViewController {

class EditPurchase: KUIViewController {

在这些屏幕上,关于键盘的绝对一切现在完全自动

你已经完成了。

呼。


*次要脚注 - 背景点击正确关闭键盘。这包括落在您的内容上的点击。这是正确的 Apple 行为。任何不寻常的变化都需要大量非常反苹果的自定义编程。

*非常小的脚注 - 因此,屏幕上的所有按钮每次都能 100% 正确工作。然而,在嵌套 (!) 滚动视图内的嵌套 (!) 容器视图与嵌套 (!) 页面视图容器 (!!!!) 的令人难以置信的模糊案例中,您可能会发现按钮似乎不起作用。在当前的 iOS 中,这似乎基本上是一个(晦涩的!)问题。如果您遇到这个令人难以置信的晦涩问题,幸运的是解决方案很简单。查看函数clearKeyboard(),只需添加一个短暂的延迟,就完成了。

@objc func clearKeyboard() {
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
        self.view.endEditing(true)
    }
}

(来自用户 @wildcat12 https://stackoverflow.com/a/57698468/294884 的一个很好的提示)

【讨论】:

  • 你知道,这不应该这么难!
  • 这是我找到的最佳答案,非常好。虽然它对我来说似乎向上移动太多,但似乎与标签栏的大小相同。
  • 我不是 100% 确定是什么导致了问题,但内容“向上移动”太多(似乎太多了 49 分)。但是当键盘隐藏时,它会返回到正确的位置。我假设它与标签栏(49pts)有关,我只是考虑了这一点,这已经解决了问题。最有可能是一个被忽略的问题,但该技巧适用于该项目。如果问题仍然存在,我会发布一个问题,谢谢
  • @Fattie 非常感谢!只是出于好奇,我之前使用的是:“ self.view.frame.origin.y -= getKeyBoardHeight(notification) ”。它工作得很好,直到突然而不是调整视图大小,它开始向上移动整个视图。既然我知道“视图”无法调整大小,这对我来说似乎是合理的。仍然不知道为什么它以前可以正常工作。这发生在我身上两次,这次是在从 XCode 7 更新到 8 之后,最后一次是在我的程序中添加了更多视图控制器之后。也许我在故事板中改变了一些东西?谢谢
  • 我不确定关于“标准方式,例如通过点击解除”的说法源自何处...... UIKit 提供的唯一键盘解除操作是在 UIScrollView 拖动时,即使它已关闭默认。无论如何,我似乎可以注释掉点击解除逻辑。
【解决方案2】:

也许它会对某人有所帮助。 您可以完全不使用界面构建器实现所需的行为

首先,您需要创建一个约束并计算安全区域插入,以便正确支持无按钮设备

var container: UIView!
var bottomConstraint: NSLayoutConstraint!
let safeInsets = UIApplication.shared.windows[0].safeAreaInsets

然后在代码中的某处对其进行初始化

container = UIView()
bottomConstraint = container.bottomAnchor.constraint(equalTo: view.bottomAnchor)

附加它以查看和激活

view.addSubview(container)

NSLayoutConstraint.activate([
       ...

       container.leadingAnchor.constraint(equalTo: view.leadingAnchor),
       container.trailingAnchor.constraint(equalTo: view.trailingAnchor),
       container.topAnchor.constraint(equalTo: view.topAnchor),
       bottomConstraint,

       ...
 ])

最后

@objc func keyboardWillShow(notification: NSNotification) {
       if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {

       if bottomConstraint.constant == 0 {
          bottomConstraint.constant = -keyboardSize.height + safeInsets.bottom     
          view.layoutIfNeeded()
       }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
       bottomConstraint.constant = 0
       view.layoutIfNeeded()
}

此外,如果您的视图是可滚动的,并且您想使用键盘将其向上移动并在键盘隐藏时返回初始位置,您可以更改视图的 contentOffset

view.contentOffset = CGPoint(x: view.contentOffset.x, y: view.contentOffset.y + keyboardSize.height - safeInsets.bottom)

用于向上滚动,并且

view.contentOffset = CGPoint(x: view.contentOffset.x, y: view.contentOffset.y - keyboardSize.height + safeInsets.bottom)

向下移动

【讨论】:

    【解决方案3】:

    来自@Fattie 的消息:

    一个细节 - (不幸的是)点击您的内容也会关闭键盘。 (他们都得到了事件。)然而,这几乎总是正确的行为;试一试。避免这种情况是没有道理的,所以忘记它并按照 Apple 流程进行吧。

    这可以通过实现以下UIGestureRecognizerDelegate的方法来解决:

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
            return !(touch.view?.isKind(of: UIControl.self) ?? true)
        }
    

    这样,如果用户触摸任何UIControl(UIButton、UITextField 等),手势识别器将不会调用clearKeyboard() 方法。

    为此,请记住在类定义或扩展中对 UIGestureRecognizerDelegate 进行子类化。然后,在 viewDidLoad() 中,您应该将手势识别器委托分配为 self。


    准备复制和粘贴代码:

    // 1. Subclass UIGestureRecognizerDelegate
    class KUIViewController: UIViewController, UIGestureRecognizerDelegate {
    
    @IBOutlet var bottomConstraintForKeyboard: NSLayoutConstraint!
    
    func keyboardWillShow(sender: NSNotification) {
        let i = sender.userInfo!
        let k = (i[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
        bottomConstraintForKeyboard.constant = k - bottomLayoutGuide.length
        let s: TimeInterval = (i[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
        UIView.animate(withDuration: s) { self.view.layoutIfNeeded() }
    }
    
    func keyboardWillHide(sender: NSNotification) {
        let info = sender.userInfo!
        let s: TimeInterval = (info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
        bottomConstraintForKeyboard.constant = 0
        UIView.animate(withDuration: s) { self.view.layoutIfNeeded() }
    }
    
    func keyboardNotifications() {
        NotificationCenter.default.addObserver(self,
            selector: #selector(keyboardWillShow),
            name: Notification.Name.UIKeyboardWillShow,
            object: nil)
        NotificationCenter.default.addObserver(self,
            selector: #selector(keyboardWillHide),
            name: Notification.Name.UIKeyboardWillHide,
            object: nil)
    }
    
    func clearKeyboard() {
        view.endEditing(true)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        keyboardNotifications()
        let t = UITapGestureRecognizer(target: self, action: #selector(clearKeyboard))
        view.addGestureRecognizer(t)
        t.cancelsTouchesInView = false
    
        // 2. Set the gesture recognizer's delegate as self
        t.delegate = self
    }
    
    // 3. Implement this method from UIGestureRecognizerDelegate
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        return !(touch.view?.isKind(of: UIControl.self) ?? true)
    }
    }
    

    【讨论】:

      【解决方案4】:

      您可以创建表格视图底部自动布局约束的出口。

      然后只需使用以下代码:

      func keyboardWillShow(sender: NSNotification) {
          let info = sender.userInfo!
          var keyboardSize = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
          bottomConstraint.constant = keyboardSize - bottomLayoutGuide.length
      
          let duration: TimeInterval = (info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
      
          UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() }
      }
      
      func keyboardWillHide(sender: NSNotification) {
          let info = sender.userInfo!
          let duration: TimeInterval = (info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
          bottomConstraint.constant = 0
      
          UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() }
      }
      

      如果您在创建底部约束时遇到问题:

      在故事板中

      • 选择您的搜索栏。
      • 在右下角您会看到 3 个图标。点击中间那个看起来像|-[]-|
      • 在该弹出窗口的顶部,有 4 个框。在底部输入 0。
      • 已创建约束!

      现在您可以将它拖到您的视图控制器中,并将其添加为插座。

      另一种解决方案是设置tableView.contentInset.bottom。但我以前没有这样做过。如果你愿意,我可以试着解释一下。

      使用插图:

      func keyboardWillShow(sender: NSNotification) {
          let info = sender.userInfo!
          let keyboardSize = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
      
          tableView.contentInset.bottom = keyboardSize
      }
      
      func keyboardWillHide(sender: NSNotification) {
          tableView.contentInset.bottom = 0
      }
      

      您可以尝试使用此代码设置插图。我自己还没有尝试过,但应该是这样的。

      编辑:根据 nacho4d 的建议更改了持续时间

      【讨论】:

      • 第二种解决方案不起作用。它什么也没做。我想我必须使用第一个解决方案,但我需要更多信息来创建底部约束。
      • 文本框出现在键盘下方,你知道怎么移动吗?
      • 插图不会应用于文本框,因此您可能不得不使用约束。如果只是表格视图,那么更改插图就足够了。这个文本框是否在表格视图单元格内?
      • 您应该使用提供的长度info[UIKeyboardAnimationDurationUserInfoKey],而不是硬编码动画持续时间 0.5。有时在没有动画的情况下会发生变化
      • 更新了 Swift 3 的代码。@JoeBlow:我认为(旧)答案不应该仅仅因为它没有更新到该语言的最新版本而被否决。您可以留下评论或编辑 更正次要 更改的答案并完成(而不是替换完整的帖子)。我认为这是对那些投入大量精力和时间来帮助他人和社区的人的侮辱,因为它不是最新版本,所以对完美有效的答案投了反对票。
      【解决方案5】:

      如果您不想自己解决这个问题,您可能会发现 TPKeyboardAvoiding 框架很有用

      只需遵循“安装说明”即可,即将适当的 .h/.m 文件拖放到您的项目中,然后将 ScrollView / TableView 设为如下子类:

      【讨论】:

      • 但这是为了客观 c
      • 我在 swift 应用程序中使用过它... ObjC 用 swift 很好地桥接了 jsut
      • 如果您阅读页面上的文档,它将指导您如何设置。但是您基本上使您的 TableView 成为 TPKeyboardAvoidingTableView 的子类。 (用图片更新了我的答案。
      • 那么文本字段、发送按钮和眼睛图标会发生什么?
      • 一切都应该遵循..试试看会发生什么
      猜你喜欢
      • 2017-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 1970-01-01
      • 2013-04-09
      相关资源
      最近更新 更多