【问题标题】:Move a view up when the keyboard covers an input field but with leaving some space between them当键盘覆盖输入字段但在它们之间留出一些空间时向上移动视图
【发布时间】:2016-03-26 14:19:38
【问题描述】:

说明

当其中一个在编辑期间被键盘覆盖时,我想向上滚动一些 UITextField。这里有很多关于 SO 的答案,有很多风格:移动视图(通过改变它的框架),修改约束,使用 UIScrollView 和 UITableView,或者使用 UIScrollView 和修改 contentInset。

我决定使用最后一个。这个也是described by Apple,有一个Swift version on SO,还有一个described on this blog,包括一个sample project on the GitHub

部分代码

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}

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

func textFieldDidEndEditing(textField: UITextField) {
    self.activeField = nil
}

func textFieldDidBeginEditing(textField: UITextField) {
    self.activeField = textField
}

func keyboardWillShow(notification: NSNotification) {
    if let activeField = self.activeField, keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
        self.scrollView.contentInset = contentInsets
        self.scrollView.scrollIndicatorInsets = contentInsets
        var aRect = self.view.frame
        aRect.size.height -= keyboardSize.size.height
        if (!CGRectContainsPoint(aRect, activeField.frame.origin)) {
            self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
        }
    }
}

func keyboardWillBeHidden(notification: NSNotification) {
    let contentInsets = UIEdgeInsetsZero
    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets
}

问题

我只想要一个简单的修改 - 在键盘和已编辑字段之间留出更多空间。因为开箱即用,它看起来像这样:

我在scrollRectToVisible 调用中修改了CGRect,但它没有任何改变。更重要的是,即使用scrollRectToVisible 注释掉这行也没有任何效果——一切都和以前一样(包括向上滚动内容)。在 iOS 9.2 上检查

如果需要,复制很简单 - 只需使用上面的 GitHub 链接下载工作代码并注释掉 scrollRectToVisible 行。

经过测试的解决方法

我尝试了解决方法,但不喜欢最终效果:

  • 增加 contentInset - 用户可以向上滚动超过 contentSize
  • UIKeyboardDidShowNotification 替换为UIKeyboardWillShowNotification,添加另一个UIKeyboardDidShowNotification 观察者,里面只有scrollRectToVisible - 可以,但是有两个向上滚动的动画,看起来不太好。

问题

  • 为什么更改contentInset(没有scrollRectToVisible 调用)会滚动scrollView 中的内容?我没有在任何文档中看到有关此类行为的信息
  • 更重要的是 - 怎么做,再向上滚动一点,在编辑的文本字段和键盘之间留出一些空间?

我错过了什么?有什么简单的方法可以解决吗?或者使用scrollView.contentSize 而不是contentInset 会更好?

【问题讨论】:

    标签: ios swift uiscrollview uikit uikeyboard


    【解决方案1】:

    我还没有找到为什么scrollRectToVisible 在上述情况下不起作用,但是我找到了其他可行的解决方案。在最底部提到的Apple文章Managing the Keyboard中有一个提示

    还有其他方法可以在滚动视图中滚动编辑区域 在一个模糊的键盘上方。而不是改变底部的内容 滚动视图的插图,您可以扩展内容的高度 按键盘高度查看,然后滚动编辑的文本 对象进入视野。

    下面的解决方案完全基于扩展内容视图的高度 (scrollView.contentSize)。它考虑了键盘隐藏时的方向变化和向后滚动。按要求工作 - 在活动字段和键盘之间有一些空间,请参见下图

    工作代码

    我已将完整的工作代码放在 GitHub 上:ScrollViewOnKeyboardShow

    var animateContenetView = true
    var originalContentOffset: CGPoint?
    var isKeyboardVisible = false
    
    let offset : CGFloat = 18
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        for case let textField as UITextField in contentView.subviews {
            textField.delegate = self
        }
    
        let notificationCenter = NSNotificationCenter.defaultCenter()
        notificationCenter.addObserver(self, selector: #selector(ViewController.keyboardWillBeShown(_:)), name: UIKeyboardWillShowNotification, object: nil)
        notificationCenter.addObserver(self, selector: #selector(ViewController.keyboardWillBeHidden(_:)), name: UIKeyboardWillHideNotification, object: nil)
    }
    
    
    func textFieldShouldReturn(textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    
    func textFieldDidEndEditing(textField: UITextField) {
        self.activeField = nil
    }
    
    
    func textFieldDidBeginEditing(textField: UITextField) {
        self.activeField = textField
    }
    
    
    func keyboardWillBeShown(notification: NSNotification) {
        originalContentOffset = scrollView.contentOffset
    
        if let activeField = self.activeField, keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            var visibleRect = self.scrollView.bounds
            visibleRect.size.height -= keyboardSize.size.height
    
            //that's to avoid enlarging contentSize multiple times in case of many UITextFields,
            //when user changes an edited text field
            if isKeyboardVisible == false {
                scrollView.contentSize.height += keyboardSize.height
            }
    
            //scroll only if the keyboard would cover a bottom edge of an
            //active field (including the given offset)
            let activeFieldBottomY = activeField.frame.origin.y + activeField.frame.size.height + offset
            let activeFieldBottomPoint = CGPoint(x: activeField.frame.origin.x, y: activeFieldBottomY)
            if (!CGRectContainsPoint(visibleRect, activeFieldBottomPoint)) {
                var scrollToPointY = activeFieldBottomY - (self.scrollView.bounds.height - keyboardSize.size.height)
                scrollToPointY = min(scrollToPointY, scrollView.contentSize.height - scrollView.frame.size.height)
    
                scrollView.setContentOffset(CGPoint(x: 0, y: scrollToPointY), animated: animateContenetView)
            }
        }
    
        isKeyboardVisible = true
    }
    
    
    func keyboardWillBeHidden(notification: NSNotification) {
        scrollView.contentSize.height = contentView.frame.size.height
        if var contentOffset = originalContentOffset {
            contentOffset.y = min(contentOffset.y, scrollView.contentSize.height - scrollView.frame.size.height)
            scrollView.setContentOffset(contentOffset, animated: animateContenetView)
        }
    
        isKeyboardVisible = false
    }
    
    
    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        coordinator.animateAlongsideTransition(nil) { (_) -> Void in
            self.scrollView.contentSize.height = self.contentView.frame.height
        }
    }
    
    
    deinit {
        let notificationCenter = NSNotificationCenter.defaultCenter()
        notificationCenter.removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        notificationCenter.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }
    

    【讨论】:

    • 这适用于文本字段。进行必要的更改后,文本视图是另一回事。
    猜你喜欢
    • 2015-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 2018-05-21
    相关资源
    最近更新 更多