【问题标题】:Smooth animation in keyboardWillShow doesn't work with UITextViewkeyboardWillShow 中的平滑动画不适用于 UITextView
【发布时间】:2020-09-08 17:09:08
【问题描述】:

对我有用的解决方案

经过几天的努力,我终于找到了解决视图动画问题的方法。对我来说 keyboardWillChangeFrameNotification 有效。解决方案在以下线程中讨论:

Move textfield when keyboard appears swift

我在堆栈视图中嵌入了一堆视图以及文本视图。我给了

@objc func keyboardWillShow(notification:NSNotification) {

    guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

    let keyboardScreenEndFrame = keyboardValue.cgRectValue
    let keyboardFrame = view.convert(keyboardScreenEndFrame, from: view.window)

    if #available(iOS 11.0, *) {
        scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardFrame.height - view.safeAreaInsets.bottom, right: 0)
    } else {
        scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardFrame.height, right: 0)
    }

    scrollView.scrollIndicatorInsets = scrollView.contentInset

    let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height + keyboardFrame.height - scrollView.bounds.size.height)
    scrollView.setContentOffset(bottomOffset, animated: true)

    UIView.animate(withDuration: 0.5, animations: { () -> Void in
        self.view.layoutIfNeeded()
    })
}

===

   extension FirstViewController: UITextViewDelegate {

    func textViewDidChange(_ textView: UITextView) {

        let estimatedSize = textView.sizeThatFits(textView.frame.size)

        if estimatedSize.height > textViewMaxHeight {
            if estimatedSize.height - textViewMaxHeight < textView.font!.lineHeight && !didExpandTextView {

                didExpandTextView = true

                var contentInset:UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.savedKbHeight, right: 0.0)

                if let v = self.tabBarController?.tabBar {
                    contentInset.bottom -= v.frame.height
                }

                scrollView.contentInset = contentInset
                scrollView.scrollIndicatorInsets = contentInset

                if textView.isFirstResponder {
                    let fr = textView.frame
                    scrollView.scrollRectToVisible(fr, animated: false)
                }
            }

            textView.isScrollEnabled = true
            textView.showsVerticalScrollIndicator = true

        } else {
            if let lineHeight = textView.font?.lineHeight, Int(estimatedSize.height / lineHeight) != numberOfLines {
                numberOfLines = Int(estimatedSize.height / textView.font!.lineHeight)

                var contentInset:UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.savedKbHeight, right: 0.0)

                print("contentInset: \(contentInset)")
                scrollView.contentInset = contentInset
                scrollView.scrollIndicatorInsets = contentInset

                if textView.isFirstResponder {
                    let fr = textView.frame
                    scrollView.scrollRectToVisible(fr, animated: false)
                }

                didExpandTextView = false
            }

            textView.isScrollEnabled = false
        }
    }

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}

【问题讨论】:

  • 快速测试表明此代码存在一些问题...即使使用您发布的动画,滚动内容似乎也被推到了不必要的高度。此外,当键盘显示时点击任一文本字段时,会将文本字段向上推并超出框架。 (我很惊讶你没有使用我为这个布局提供的键盘代码。)
  • 我已经试过你的代码了,结果还是一样。使用您的代码,约束和动画都显示与我上面的代码相同的问题。请尝试插入一个 textview 并将其高度限制为
  • 这里是使用您的代码显示结果的链接。 imgur.com/a/EklKnaw
  • 如果您仍然遇到此问题,我认为问题可能是由于您正在执行的“自动垂直居中”。您可能需要采取不同的方法。
  • 我还是有问题。我没有明白你所说的自动垂直居中的意思。你能用代码详细说明一下吗?

标签: ios swift uiscrollview uitextview uistackview


【解决方案1】:

试试这个方法:

    func textViewDidChange(_ textView: UITextView) {

        let estimatedSize = textView.sizeThatFits(textView.frame.size)

        textView.isScrollEnabled = estimatedSize.height > textViewMaxHeight

        if !textView.isScrollEnabled {
            let maxBottom = self.view.frame.height - self.savedKbHeight

            // Use following line if you have Extended Edges set
            // let maxBottom = self.view.frame.height - self.savedKbHeight - topLayoutGuide.length - bottomLayoutGuide.length

            var r = self.textView.frame
            r.size.height = estimatedSize.height
            let tvBottom = self.scrollView.convert(r, to: self.view).maxX
            if tvBottom > maxBottom {
                self.scrollView.scrollRectToVisible(r, animated: true)
            }
        }
    }

编辑 注意:这不是生产就绪代码。影响布局/定位的因素有很多,并不能保证“放入”在所有情况下都有效。

【讨论】:

  • 谢谢,现在好多了。最后一个问题是,当按下返回键时,视图会跳转,然后自行调整。看到这个imgur.com/a/iFqlPJi我们如何解决这个问题?
  • @Raymond - 重新审视我的 GitHub 存储库:github.com/DonMag/CenteredScroll ... 第二个选项卡(黄色/橙色背景)已实现 textViewDidChange(),但我没有看到你在做什么看到。
  • 没错,它没有那个错误,但是这个错误中还有另一个错误。每次返回后它都会隐藏并按下登录按钮。我们可以在文本视图扩展并用新行向上推送视图时保持登录按钮一直显示吗?
  • @Raymond - “我们可以保持登录按钮显示...” - 当然。使用按钮框架而不是文本视图框架。
  • 我希望我能修复它。我尝试在 textViewDidChange 中使用 var r = self.loginButton.frame ,但它对视图做了一些有趣的俯卧撑。你能在代码中做吗?我仍在努力理解这整个滚动内容。
猜你喜欢
  • 1970-01-01
  • 2013-10-08
  • 1970-01-01
  • 2019-10-02
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多