【问题标题】:Is it possible to use a pageviewcontroller inside a scrollview?是否可以在滚动视图中使用 pageviewcontroller?
【发布时间】:2018-08-19 05:40:15
【问题描述】:

我有一个 pageviewcontroller,它可以水平滚动,显示问题和答案。

pageviewcontroller 的 contentView 填充了一个滚动视图,其中有一些堆栈视图,其中一个有一个 textView,供用户输入。

当用户点击 textView 时,键盘会弹出,有时会覆盖 textView。当我尝试垂直滚动时,正如我希望滚动视图允许的那样,滚动视图没有响应,我只能水平滚动到下一个页面视图。

问题是当用户在文本视图中点击后弹出键盘时,文本视图可能隐藏在键盘下方,因此用户看不到他正在输入的内容。我希望在敲击键盘时让视图向上滚动,以便用户可以看到正在输入的内容。

【问题讨论】:

  • 所以要明确你的问题不是标题中的问题,对吧?
  • 我认为问题与垂直滚动视图不与 pageviewcontroller 一起使用有关。我没有意识到这是约束和键盘设置的问题。当键盘弹出时,我尝试向上滚动以便看到 textview,但它不会垂直移动。

标签: ios swift uiscrollview uipageviewcontroller


【解决方案1】:

使用通知观察者来处理键盘显示和隐藏事件:

// keyboard will show
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowAction(_:)), name: .UIKeyboardWillShow, object: nil)

// keyboard will hide
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideAction(_:)), name: .UIKeyboardWillHide, object: nil)

当键盘即将出现时,你可以这样做:

// keyboard will show action
@objc private func keyboardWillShowAction(_ notification: NSNotification) {

    currentScrollViewOffset = scrollView.contentOffset

    let keyboardFrame = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue
    let keyboardHeight = keyboardFrame?.cgRectValue.height
    let padding: CGFloat = 32

    // if text field view would be obstructed by keyboard, apply minimum-needed scroll view offset
    if activeTextFieldMaxYOnScreen! > (UIScreen.main.bounds.height - keyboardHeight! - padding) {

        let temporaryOffset = activeTextFieldMaxYOnScreen! - (UIScreen.main.bounds.height - (keyboardHeight! + padding))
        scrollView.setContentOffset(CGPoint(x: 0, y: currentScrollViewOffset.y + temporaryOffset), animated: true)

    }

    scrollView.addGestureRecognizer(tapToDismissKeyboard)
    scrollView.isScrollEnabled = false

}

然后在键盘关闭时恢复:

// keyboard will hide action
@objc private func keyboardWillHideAction(_ notification: NSNotification) {

    scrollView.setContentOffset(currentScrollViewOffset, animated: true)
    scrollView.removeGestureRecognizer(tapToDismissKeyboard)
    scrollView.isScrollEnabled = true

}

deinit 方法中删除观察者也是一个好习惯:

NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)

注意currentScrollViewOffsetactiveTextFieldMaxYOnScreen 是视图控制器的实例属性。要获取activeTextFieldMaxYOnScreen 值,您可以从文本字段委托中获取它:

// text field should begin editing
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {

    // convert text field line's max-y to perceived max-y on the screen
    let line = textField.viewWithTag(123)
    activeTextFieldMaxYOnScreen = (line?.convert((line?.bounds.origin)!, to: nil).y)! + (line?.frame.height)!
    return true

}

【讨论】:

    【解决方案2】:

    您应该在堆栈视图中设置底部约束 然后像这样控制在你的班级中拖动它:

    class yourClassViewController: UIViewController {
    // MARK: Properties
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
    

    然后在你的 viewDidLoad 方法中这样写:

     override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.keyboardNotification(notification:)),
                                               name: NSNotification.Name.UIKeyboardWillChangeFrame,
                                               object: nil)
    
    
        // Do any additional setup after loading the view.
    }
    

    以下:

       deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    @objc func keyboardNotification(notification: NSNotification) {
        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
            let endFrameY = endFrame?.origin.y ?? 0
            let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            if endFrameY >= UIScreen.main.bounds.size.height {
                self.bottomConstraint?.constant = 0.0
            } else {
                self.bottomConstraint?.constant = endFrame?.size.height ?? 0.0
            }
            UIView.animate(withDuration: duration,
                           delay: TimeInterval(0),
                           options: animationCurve,
                           animations: { self.view.layoutIfNeeded() },
                           completion: nil)
        }
    }
    

    对于 touchsBegan 方法中的消失键盘,您应该这样写:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
              self.view.endEditing(true)
    }
    

    【讨论】:

      【解决方案3】:

      这就是所谓的“键盘管理” 你必须:

      1) 设置键盘出现和键盘消失动作的观察者

      2) 当键盘出现时更新底部约束。

      3) 当键盘消失时再次更新。

      4) 删除观察者,当视图消失时。

      分步说明,例如这里:Move view with keyboard using Swift

      但我的建议是按照说明更新约束,而不是起源。或者,也许,您可以为内容设置偏移量

      【讨论】:

      • 我按照“使用 Swift 使用键盘移动视图”的说明进行操作,效果很好。非常感谢。对于“完成”按钮关闭键盘而不是点击“返回”,您是否有一个简单的建议解决方案?我见过几种方法可以做到这一点,但它们似乎有点复杂。
      • 最简单的方法是键盘管理器框架。例如github.com/hackiftekhar/IQKeyboardManager
      • 我发现因为我有一个堆栈视图,当键盘出现时,文本字段有时会变小,然后我看不到里面有什么,所以我很想知道如果上述解决方案可以与“内容的设置和偏移”配合使用。关于如何做到这一点的任何建议?
      • 你必须向上推滚动视图的底部约束。所有内容也会被推送
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2011-07-28
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多