当键盘出现时移动按钮 Swift

8
在UIViewController中,我有几个文本字段和一个按钮在UIViewController的底部。对于按钮,我设置了一个常量为0的底部约束。然后,我从底部约束到UIViewController创建了一个outlet。
当我运行我的代码时,按钮不会向上移动。我在stackoverflow上看到了建议,说我应该添加UIScrollView,但这意味着我必须删除UIViewController上的所有对象,放置UIScrollView,然后再将我的对象放回到UIVIewController上。
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!

// When tapping outside of the keyboard, close the keyboard down
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    self.view.endEditing(true)
}

// Stop Editing on Return Key Tap. textField parameter refers to any textfield within the view
func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}

// When keyboard is about to show assign the height of the keyboard to bottomConstraint.constant of our button so that it will move up
func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize: CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            bottomConstraint.constant = keyboardSize.size.height
            view.setNeedsLayout()
        }
    }
}

// When keyboard is hidden, move the button to the bottom of the view
func keyboardWillHide(notification: NSNotification) {
    bottomConstraint.constant = 0.0
    view.setNeedsLayout()
}

enter image description here

5个回答

9

解决这个问题的典型方法是使用以下代码将键盘移动:

在 ViewController 类中:

  func keyboardWillShow(notification: NSNotification) {

        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            if view.frame.origin.y == 0{
                let height = keyboardSize.height

                self.view.frame.origin.y += height
            }

        }

    }

    func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            if view.frame.origin.y != 0 {
                let height = keyboardSize.height
                self.view.frame.origin.y -= height
            }

        }
    }

在ViewDidLoad方法中:

  NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillShow:"), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
  NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillHide:"), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
请注意: 你尝试解决问题的方法是不被允许的。在上述代码中,如果你将view改为你的按钮变量名,按钮会弹起然后掉落。这是因为自动布局和编程布局不能同时使用,只能选择其中一种。你需要通过编程方式创建该按钮(使用CGRect),然后使用上述代码仅移动该按钮以响应键盘按下事件(通过将view更改为你的按钮变量名)。
   func keyboardWillShow(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if view.frame.origin.y == 0{
            let height = keyboardSize.height
            self.yourBtn.frame.origin.y += height
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if view.frame.origin.y != 0 {
            let height = keyboardSize.height
            self.yourBtn.frame.origin.y -= height
        }
    }
}

要在程序中创建按钮,您需要使用类似于以下代码的代码:
myButton.frame = CGRect(...)

请问您为什么说我这样做很奇怪?您认为这会暴露我的代码给某些漏洞吗? - bibscy
@bibscy 我解决了。请查看我的更新答案。说实话,我确实认为当文本字段本身占据屏幕的一半以上并且按钮直接覆盖它们时,这将暴露出布局错误(特别是在iPad布局或较小的iPhone上)。为了解决这个问题,我建议将您的文本字段嵌入到滚动视图中。 - Ryan Cocuzzo
我之前不小心交换了keyboardWillShowkeyboardWillHide的实现,但我已经修复了这个问题,所以它不应该再让东西消失了。 - Ryan Cocuzzo
我已经将文本字段嵌入滚动视图中,并通过编程添加了按钮。但是,存在以下问题:1. 当我开始编辑文本字段时,按钮不会随着键盘一起上移。2. 当我点击键盘外部时,键盘不会隐藏(在添加滚动视图之前是可以隐藏的)。 - bibscy
我尝试了你建议的方法,但按钮仍然停留在滚动视图下面。请查看我的新问题http://stackoverflow.com/questions/42543593。非常感谢。 - bibscy

5

为了补充Ryan上面的答案,这可以完全使用自动布局来完成,无需使用框架和CGRect。

Swift 5

在您的视图中,按照通常的方式约束按钮,但添加一个约束的引用,以便在键盘隐藏/显示时进行修改:

var bottomButtonConstraint = NSLayoutConstraint()

bottomButtonConstraint = yourButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -12)
bottomButtonConstraint.isActive = true

在您的视图控制器中的 viewDidLoad() 函数内:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)

在你的ViewController中:

@objc private func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        self.yourCustomView.bottomButtonConstraint.constant -= keyboardSize.height
    }
}

@objc private func keyboardWillHide(notification: NSNotification) {
    self.yourCustomView.bottomButtonConstraint.constant = -12
}

safeArea 未定义 :/ - Rami Alloush
1
错别字。请尝试使用 safeAreaLayoutGuide - Justin Vallely
view.safeAreaLayoutGuide.bottomAnchor - iMinion

3

您需要在viewDidLoad中添加观察者来调用您的函数:

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillShow), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillHide), name: UIKeyboardDidHideNotification, object: nil)

我添加了观察者,它起作用了。然而,@Ryan说我的实现会因为不同的iPhone屏幕尺寸而暴露出意外的行为。他提出了一种方法,但它没有起作用。按钮不随键盘升起,键盘在点击外部时不再放弃,当在键盘外部点击时,我现在创建的编程按钮不再显示,在iPhone 6上,按钮固定在屏幕底部,但在其他屏幕上看起来不同,nextButton.frame = CGRectMake(0, 620, 375, 50)。你能帮忙吗? - bibscy

0
考虑使用这个pod:https://cocoapods.org/pods/IQKeyboardManager 在AppDelegate.swift中,只需导入IQKeyboardManagerSwift框架并启用IQKeyboardManager。
import IQKeyboardManagerSwift
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  IQKeyboardManager.shared.enable = true

  return true
}

}


IQKeyboardManager会在文本框被键盘遮挡时更新文本框内容。但它并没有回答这个问题。 - Ankur Lahiry

0
我知道我来晚了一点,但由于上一个回答是在大约2年前,我认为我可以为这个问题提供更多的价值。 我的示例适用于Swift 5.5+,但较低版本也应该可以使用。
在我的场景中,我需要一种更通用、可配置的解决方案,它可以让我在项目中的任何UIViewController类中移动视图并控制间距。
以下是使其工作所需的步骤:
第一步:
为您的视图底部约束创建outlet,并为键盘和源视图定义自定义间距:
@IBOutlet weak var <<yourViewName>>BottomConstraint: NSLayoutConstraint! // Your view's bottom constraint, that should be connected to safe/area or superview

private let <<yourViewName>>BottomSpacing: CGFloat = 56 // Spacing between view and safe area/superview when keyboard is hidden
private let <<yourViewName>>KeyboardSpacing: CGFloat = 22 // Spacing between active keyboard and your view

第二步:
在我们的视图控制器中注册观察者,以跟踪键盘的显示/隐藏(您可以根据需要在viewDidLoadinit中调用此方法)。请注意,在旧版本的Swift中,您必须手动删除观察者,例如在deinit中。
private func addKeyboardObservers() {
    // Notifications for when the keyboard show/hides
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(self.keyboardWillShow),
                                           name: UIResponder.keyboardWillShowNotification,
                                           object: nil)
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(self.keyboardWillHide),
                                           name: UIResponder.keyboardWillHideNotification,
                                           object: nil)
}

第三步:
将以下代码添加到您的UIViewController类中。这段代码将作为NotificationCenter观察者的结果被调用:
@objc private func keyboardWillShow(_ notification: NSNotification) {
    moveViewWithKeyboard(notification: notification,
                         keyboardWillShow: true,
                         viewBottomConstraint: <<yourViewName>>BottomConstraint,
                         activeKeyboardToViewSpacing: <<yourViewName>>KeyboardSpacing,
                         hiddenKeyboardToViewSpacing: <<yourViewName>>BottomSpacing)
}

@objc private func keyboardWillHide(_ notification: NSNotification) {
    moveViewWithKeyboard(notification: notification,
                         keyboardWillShow: false,
                         viewBottomConstraint: <<yourViewName>>BottomConstraint,
                         activeKeyboardToViewSpacing: <<yourViewName>>KeyboardSpacing,
                         hiddenKeyboardToViewSpacing: <<yourViewName>>BottomSpacing)
}

第四步:
拼图的最后一部分是一个方法,它获取键盘大小,计算间距并相应地移动视图。
通常,在我的项目中,我有一个名为UIViewController+Keyboard.swift的文件,其中包含这段代码作为extensionUIViewController。您还可以添加其他与键盘管理相关的代码,例如在用户点击周围时隐藏键盘,如下面的示例所示:
extension UIViewController {

func hideKeyboardWhenTappedAround() {
    let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
    tap.cancelsTouchesInView = false
    view.addGestureRecognizer(tap)
}

@objc
func dismissKeyboard() {
    view.endEditing(true)
}

func moveViewWithKeyboard(notification: NSNotification,
                          keyboardWillShow: Bool,
                          viewBottomConstraint: NSLayoutConstraint,
                          activeKeyboardToViewSpacing: CGFloat,
                          hiddenKeyboardToViewSpacing: CGFloat) {
    
    guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
    let keyboardHeight = keyboardSize.height
    let keyboardAnimationDuration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
    let keyboardAnimationCurve = UIView.AnimationCurve(rawValue: notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! Int)!
    
    // Modifying spacing constants
    if keyboardWillShow {
        let safeAreaExists = self.view?.window?.safeAreaInsets.bottom != 0
        // Default value in case something goes wrong with bottom spacings
        let bottomConstant: CGFloat = 20
        viewBottomConstraint.constant = keyboardHeight + (safeAreaExists ? 0 : bottomConstant) + activeKeyboardToViewSpacing
    } else {
        viewBottomConstraint.constant = hiddenKeyboardToViewSpacing
    }
    
    // Animating the view the same way the keyboard animates
    let animator = UIViewPropertyAnimator(duration: keyboardAnimationDuration, curve: keyboardAnimationCurve) { [weak self] in
        self?.view.layoutIfNeeded()
    }
    
    animator.startAnimation()
} }

实施所有这些步骤后,您应该获得所需的可重用行为和可配置的间距。 希望对您有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接