使用 UITextField 自定义自动 UIScrollView 滚动

Customize automatic UIScrollView scrolling with UITextField

我想在显示键盘时自定义滚动偏移量。正如您在 GIF 中看到的那样,文本字段非常靠近键盘,我希望有一个自定义位置。 "Name" 文本字段应该有 50px 的距离,"Loan Title" 文本字段应该滚动到我的 UIScrollView 的底部。

为了能够滚动经过键盘,我正在更改 UIScrollView 插图。奇怪的是 iOS 自动滚动到 firstResponder 文本字段(参见 GIF)。

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

@objc func keyboardWillShow(notification: NSNotification) {
    // get the Keyboard size
    let userInfo = notification.userInfo!
    let keyboardEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue

    // update edge insets for scrollview
    self.mainScrollView.scrollIndicatorInsets.bottom = keyboardEndFrame.height - self.view.layoutMargins.bottom
    self.mainScrollView.contentInset.bottom = keyboardEndFrame.height - self.view.layoutMargins.bottom
}

我已经尝试使用 UITextfieldDelegate 方法:textFieldDidBeginEditing(_ textField: UITextField)
我还尝试使用此处描述的 Apple 方式:

None 这些方法让我可以自定义自动滚动位置。事实上,它有点凌驾于每一次尝试之上。有谁知道解决这个问题的方法吗?

您可以通过将 automaticallyAdjustsScrollviewInsets 设置为 false 来防止您的视图控制器自动滚动,如 here 所述。

实现键盘回避也非常简单。你可以看看怎么做 .

我认为没有任何方法可以保持自动定位并应用您自己的自定义偏移量。您可以尝试将文本字段包含在另一个更大的视图中,并使该更大的视图成为第一响应者,但这充其量只是一种黑客攻击。

我自己找到了解决办法。问题是自动滚动(动画)干扰了我的 scrollRectToVisible 调用。把它放在 async 中解决了这个问题。

现在看起来类似于:

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

@objc func keyboardWillShow(notification: NSNotification) {
    // get the Keyboard size
    let userInfo = notification.userInfo!
    let keyboardEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue

    // update edge insets for scrollview
    self.mainScrollView.scrollIndicatorInsets.bottom = keyboardEndFrame.height - self.view.layoutMargins.bottom
    self.mainScrollView.contentInset.bottom = keyboardEndFrame.height - self.view.layoutMargins.bottom

    var frame = CGRect.zero

    if nameTextField.isFirstResponder {
        frame = CGRect(x: nameTextField.frame.origin.x, y: nameTextField.frame.origin.y + 50, width: nameTextField.frame.size.width, height: nameTextField.frame.size.height)
    }
    if titleTextField.isFirstResponder {
        frame = CGRect(x: titleTextField.frame.origin.x, y: titleTextField.frame.origin.y + titleShortcutsCollectionView.frame.height + 25, width: titleTextField.frame.size.width, height: titleTextField.frame.size.height)
    }

    DispatchQueue.main.async {
        self.mainScrollView.scrollRectToVisible(frame, animated: true)
    }
}