UIScrollView 在键盘关闭时停止滚动

Posted

技术标签:

【中文标题】UIScrollView 在键盘关闭时停止滚动【英文标题】:UIScrollView stops scrolling on keyboard dismiss 【发布时间】:2017-01-07 07:34:27 【问题描述】:

我目前正在使用 Swift 3 为 iPhone 开发一个应用程序,但是我遇到了滚动视图的问题。 在选择文本字段并出现键盘之前,滚动视图正常工作(即:我可以上下滚动),但是在关闭键盘后,滚动视图不允许我再滚动,这是我的问题。 注意:如果我再次选择一个文本字段并使键盘出现,它可以正常工作,并且一旦再次关闭就会停止工作。

我检查了滚动视图的 isScrollEnabled 属性,它似乎已启用。不幸的是,我仍然不太熟悉滚动视图的所有细节,并且似乎无法弄清楚它为什么停止工作。

任何关于我可以在哪里查看的帮助或指示将不胜感激。

编辑:有很多代码,但这里是与滚动视图和键盘相关的缩小部分:

class ScrollViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate 
//Scroll view
    @IBOutlet weak var scrollView: UIScrollView!
    //UIView inside the scroll view
    @IBOutlet weak var contentView: UIView!
    //Save button on the top right corner
    @IBOutlet weak var saveButton: UIBarButtonItem!
    //Text field being editted
    var activeTextField:UITextField?

    fileprivate var contentInset:CGFloat?
    fileprivate var indicatorInset:CGFloat?

override func viewDidLoad() 
        contentInset = scrollView.contentInset.bottom
        indicatorInset = scrollView.scrollIndicatorInsets.bottom

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ScrollViewController.keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillShow,
                                               object: nil)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ScrollViewController(_:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)


func adjustInsetForKeyboardShow(_ show: Bool, notification: Notification) 
        let userInfo = notification.userInfo ?? [:]
        let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let adjustmentHeight = (keyboardFrame.height + 20) * (show ? 1 : -1)

        scrollView.contentInset.bottom = (contentInset! + adjustmentHeight)
        scrollView.scrollIndicatorInsets.bottom = (indicatorInset! + adjustmentHeight)
    

    func keyboardWillShow(_ notification: Notification) 
        adjustInsetForKeyboardShow(true, notification: notification)
    

    func keyboardWillHide(_ notification: Notification) 
        adjustInsetForKeyboardShow(false, notification: notification)
    

    //Tap gesture to dismiss the keyboard
    @IBAction func hideKeyboard(_ sender: AnyObject) 
        self.view.endEditing(false)
    

       deinit 
        NotificationCenter.default.removeObserver(self);
    

【问题讨论】:

代码会很有帮助。 键盘的出现可能会改变滚动视图的边界,但没有代码就很难分辨。 【参考方案1】:

我已经创建了 UIViewController 的扩展并创建了 scrollView 的方法。只需要从 viewWillAppear() 和 viewDidDisappear() 调用

extension UIViewController  

    func registerForKeyboradDidShowWithBlock (scrollview:UIScrollView ,block: ((CGSize?)  -> Void)? = nil )
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardDidShow, object: nil, queue: nil)  (notification) in
            if let userInfo = (notification as NSNotification).userInfo 
                if let keyboarRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue 
                    if self.view.findFirstResponder() != nil 
                        let  keyboarRectNew = self.view .convert(keyboarRect, to: self.view)
                        let scrollViewSpace = scrollview.frame.origin.y + scrollview.contentOffset.y
                        let textFieldRect:CGRect =  self.view.findFirstResponder()!.convert(self.view.findFirstResponder()!.bounds, to: self.view)
                        let textFieldSpace = textFieldRect.origin.y + textFieldRect.size.height
                        let remainingSpace =  self.view.frame.size.height - keyboarRectNew.size.height

                        if  scrollViewSpace + textFieldSpace > remainingSpace 
                            let gap =  scrollViewSpace + textFieldSpace - remainingSpace
                            scrollview .setContentOffset(CGPoint(x: scrollview.contentOffset.x, y: gap), animated: true)
                        
                    
                
            
            block?(CGSize.zero)

        
    

    func registerForKeyboardWillHideNotificationWithBlock ( scrollview:UIScrollView ,block: ((Void) -> Void)? = nil) 
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: nil, using:  (notification) -> Void in
            scrollview.scrollRectToVisible(CGRect(x: 0, y: 0, width: 0, height: 0), animated: true)
            scrollview.contentOffset = CGPoint(x: 0, y: 0)
            scrollview.contentInset =  UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)
            scrollview.scrollIndicatorInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0);

            block?()
        )
    

    func deregisterKeyboardShowAndHideNotification ()

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

        self.view.findFirstResponder()?.resignFirstResponder()
    

我已经创建了UIView 的扩展来创建查找第一响应者方法。

extension UIView 
    func findFirstResponder() -> UIView? 
        if self.isFirstResponder 
            return self
        
        for subview: UIView in self.subviews 
            let firstResponder = subview.findFirstResponder()
            if nil != firstResponder 
                return firstResponder
            
        
        return nil
       

【讨论】:

我确实尝试添加这个,但 UIView 似乎没有 findFirstResponder() 函数,至少在 Swift 3 中是这样。知道需要用什么替换吗? 您好,对不起。我想添加此方法。这是我为查找第一响应者而创建的。请看下面的代码【参考方案2】:

创建 UIView 的 Extension 并记下这个方法。

extension UIView 
    func findFirstResponder() -> UIView? 
        if self.isFirstResponder 
            return self
        
        for subview: UIView in self.subviews 
            let firstResponder = subview.findFirstResponder()
            if nil != firstResponder 
                return firstResponder
            
        
        return nil
       

【讨论】:

【参考方案3】:

它们是 Objective C 中的第三方库,用于处理带有滚动视图、集合视图和表格视图的键盘。库的名称是 tpkeyboardavoidingscrollview。如果可能,尝试嵌入它。

【讨论】:

感谢您的推荐,但我尽量避免使用外部库,以便尽可能多地学习

以上是关于UIScrollView 在键盘关闭时停止滚动的主要内容,如果未能解决你的问题,请参考以下文章

当我在显示的键盘上单击文本字段两次时,iOS UIScrollView 无法滚动

UIScrollView 在 iOS 7 上停止滚动

为啥显示键盘时 UIScrollView 不滚动?

键盘出现时如何滚动 UIScrollView?

当另一个 UIScrollView 滚动时停止动画 UIScrollView

图像缩放时在 UIScrollView 中停止滚动图像