【问题标题】:Tableview scroll content when keyboard shows键盘显示时Tableview滚动内容
【发布时间】:2014-08-23 03:23:30
【问题描述】:

我有一个带有文本字段和文本视图的表格视图。我已经按照这个苹果示例代码https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

的建议实现了这段代码
@IBOutlet var myTableView: UITableView
func keyboardWasShown (notification: NSNotification)
{
    println("keyboard was shown")
    var info = notification.userInfo
    var keyboardSize = info.objectForKey(UIKeyboardFrameBeginUserInfoKey).CGRectValue().size

    myTableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
    myTableView.scrollIndicatorInsets = myTableView.contentInset
}

func keyboardWillBeHidden (notification: NSNotification)
{
    println("keyboard will be hidden")
    myTableView.contentInset = UIEdgeInsetsZero
    myTableView.scrollIndicatorInsets = UIEdgeInsetsZero
}
  override func viewDidLoad() {

    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)

}

当我点击滚动视图的“文本”时,会转到屏幕顶部的正上方,但是当我松开键盘时,它仍然会向上滚动。这就像 insets 属性在第一次之后无法修改一样。我的错误是什么?

【问题讨论】:

    标签: ios uitableview uiscrollview keyboard swift


    【解决方案1】:

    尝试保持编辑索引路径 editingIndexPath Getting index path 并将 tableview 滚动到该索引路径

    func keyboardWasShown (notification: NSNotification)
        {
            println("keyboard was shown")
            var info = notification.userInfo
            var keyboardSize = info.objectForKey(UIKeyboardFrameBeginUserInfoKey).CGRectValue().size
    
            var contentInsets:UIEdgeInsets
    
            if UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation) {
    
                contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0);
            }
            else {
                contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.width, 0.0);
    
            }
    
            myTableView.contentInset = contentInsets
    
            myTableView.scrollToRowAtIndexPath(editingIndexPath, atScrollPosition: .Top, animated: true)
            myTableView.scrollIndicatorInsets = myTableView.contentInset
        }
    

    【讨论】:

    • 不行!第一次滚动后它仍然保持固定。我不明白为什么keyboardWillBeHidden 函数的分配不起作用。是否有可能是因为 tableview 是使用 Interface Builder 构建的,并且可能有一些特定的选项?
    • @Andorath 你能把样品递给我吗?
    • 我怎样才能给你的样品打磨?
    • 我也面临同样的问题。任何人都可以建议。
    • 请查看此链接。希望对你有帮助code.tutsplus.com/tutorials/…
    【解决方案2】:

    使用以下代码获取 Indexpath 并根据 UIKeyboard Height 更改 UITableview 内容偏移量

    func keyboardWillShow(notification: NSNotification) {
        if ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {
            //self.view.frame.origin.y -= keyboardSize.height
            var userInfo = notification.userInfo!
            var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
            keyboardFrame = self.view.convert(keyboardFrame, from: nil)
    
            var contentInset:UIEdgeInsets = self.tbl.contentInset
            contentInset.bottom = keyboardFrame.size.height
            self.tbl.contentInset = contentInset
    
            //get indexpath
            let indexpath = NSIndexPath(forRow: 1, inSection: 0)
            self.tbl.scrollToRowAtIndexPath(indexpath, atScrollPosition: UITableViewScrollPosition.Top, animated: true)
        }
    }
    
    func keyboardWillHide(notification: NSNotification) {
        if ((notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
            let contentInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
            self.tbl.contentInset = contentInset
        }
    }
    

    【讨论】:

      【解决方案3】:
      override func viewDidLoad() {
          super.viewDidLoad()
      
          NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
      
          NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
      
      }
      
      func keyboardWillShow(_ notification:Notification) {
      
          if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
              tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
          }
      }
      func keyboardWillHide(_ notification:Notification) {
      
          if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
              tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
          }
      }
      

      【讨论】:

      • 这是最好的答案。谢谢。
      • 请移除通知观察者
      • 我注意到如果您先关闭键盘然后再打开,您可能会收到错误的高度。为避免这种情况,只需使用 UIKeyboardFrameEndUserInfoKey 而不是 UIKeyboardFrameBeginUserInfoKey。
      • 最佳答案,我也可以建议取消通知,拜托。 deinit { NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) }
      • 请注意,不再需要删除观察者:stackoverflow.com/a/40339926/1650180
      【解决方案4】:
          func keyboardWillShow(notification: NSNotification) {
          if ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()) != nil {
              //self.view.frame.origin.y -= keyboardSize.height
              var userInfo = notification.userInfo!
              var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
              keyboardFrame = self.view.convertRect(keyboardFrame, fromView: nil)
      
              var contentInset:UIEdgeInsets = self.tbl.contentInset
              contentInset.bottom = keyboardFrame.size.height
              self.tbl.contentInset = contentInset
      
              //get indexpath
              let indexpath = NSIndexPath(forRow: 1, inSection: 0)
              self.tbl.scrollToRowAtIndexPath(indexpath, atScrollPosition: UITableViewScrollPosition.Top, animated: true)
          }
      }
      
      func keyboardWillHide(notification: NSNotification) {
          if ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()) != nil {
              let contentInset:UIEdgeInsets = UIEdgeInsetsZero
              self.tbl.contentInset = contentInset
          }
      }
      

      【讨论】:

      • 上述做法的问题是设置contentInset后第一次点击table view,section中>0指向section = 0。即在didSelect处indexPath值对应section中的值0,尽管您已单击该部分中的行 > 0。这只发生在第一次单击时。
      【解决方案5】:

      ** 没有滚动到键盘显示的上方**

          override func viewDidLoad() {
          super.viewDidLoad()
      
          NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
      
          NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
      }
      
      func keyboardWillShow(_ notification:Notification) {
      
          if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
              tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
          }
      }
      
      func keyboardWillHide(_ notification:Notification) 
      {
          if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
              tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
          }
      }
      

      【讨论】:

      • 和 Zany 的回答一样。
      【解决方案6】:

      使用这个很棒的扩展(为 Swift 4.2 更新),

      extension UIViewController {
      
          func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil) {
              _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil, using: { notification -> Void in
                  let userInfo = notification.userInfo!
                  let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
                  let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top, left: scrollView.contentInset.left, bottom: keyboardSize.height, right: scrollView.contentInset.right)
      
                  scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
                  block?(keyboardSize)
              })
          }
      
          func registerForKeyboardWillHideNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil) {
              _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: nil, using: { notification -> Void in
                  let userInfo = notification.userInfo!
                  let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
                  let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top, left: scrollView.contentInset.left, bottom: 0, right: scrollView.contentInset.right)
      
                  scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
                  block?(keyboardSize)
              })
          }
      }
      
      extension UIScrollView {
      
          func setContentInsetAndScrollIndicatorInsets(_ edgeInsets: UIEdgeInsets) {
              self.contentInset = edgeInsets
              self.scrollIndicatorInsets = edgeInsets
          }
      }
      

      并从相应的 ViewController 如下所述使用,

      @IBOutlet weak var tableview: UITableView!
      
      override func viewDidLoad() {
              super.viewDidLoad()
              registerForKeyboardWillShowNotification(tableview)
              registerForKeyboardWillHideNotification(tableview)
      
              /* use the above functions with
                 block, in case you want the trigger just after the keyboard
                 hide or show which will return you the keyboard size also.
               */
      
              registerForKeyboardWillShowNotification(tableView) { (keyboardSize) in
                  print("size 1 - \(keyboardSize!)")
              }
              registerForKeyboardWillHideNotification(tableView) { (keyboardSize) in
                  print("size 2 - \(keyboardSize!)")
              }
      
          }
      

      【讨论】:

      • 不错的扩展。请更新到 Swift 4.2 并展示使用 block.Thx 的示例。
      • 更新了 Swift 4.2 的答案
      • extension UIScrollView 之前缺少一个}(并且缩进略有偏离)
      • 真的很棒的扩展,但是有没有其他人注意到底部边缘插图的值不太正确?我最终在视图和键盘之间出现了额外的死区(请参阅here)。视图与安全区域对齐。我必须使用函数return height - (height > 200 ? 50 : 30) 调整keyboardSize.height 的值。
      • 注意删除观察者,否则可能会出现内存泄漏。请参阅:developer.apple.com/library/archive/releasenotes/Foundation/…
      【解决方案7】:

      我对此有一个小想法,我用 swift 编写了一个扩展。 随意贡献并在您自己的项目中使用它:

      https://github.com/joluc/AutoAdjust

      import Foundation
      import UIKit
      
      extension UITableView {
          // I am working on a way to deinit the observers when the tableview also is deiniting.
          // If you have ideas, feel free to help out!
          func setupAutoAdjust()
          {
              NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardshown), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
              NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardhide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
          }
          @objc func keyboardshown(_ notification:Notification)
          {
              if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
                  self.fitContentInset(inset: UIEdgeInsetsMake(0, 0, keyboardSize.height, 0))
              }
          }
          @objc func keyboardhide(_ notification:Notification)
          {
              if ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {
                  self.fitContentInset(inset: .zero)
              }
      
          }
          func fitContentInset(inset:UIEdgeInsets!)
          {
              self.contentInset = inset
              self.scrollIndicatorInsets = inset
          }
      }
      

      【讨论】:

        【解决方案8】:

        您也可以使用 textfield/textViewDidBeginEditing。我已经将它用于聊天日志 tableView。

        func textViewDidBeginEditing(_ textView: UITextView) {
        
               if self.chatMessages.count >= 1 {
                 let section = self.chatMessages.count - 1
                 let row = self.chatMessages[section].count - 1
                 let indexPath = IndexPath(row: row, section: section )
                 self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
               }
        }
        

        【讨论】:

        • 我删除了 DispatchQueue 部分,因为它不需要。这个对我有用。分享你的一些代码。
        【解决方案9】:

        对于 Swift 4.2

        在 UIViewController 的 viewDidLoad() 中:

        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        

        以及选择器的实现:

        @objc private func keyboardWillShow(notification: NSNotification) {
            if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
                tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
            }
        }
        
        @objc private func keyboardWillHide(notification: NSNotification) {
            tableView.contentInset = .zero
        }
        

        【讨论】:

        • 这很好用,只需要添加滚动到行功能
        • let indexPath:IndexPath = IndexPath(row: #, section: #) tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
        • 我也试过了。但我的文本字段没有向上移动
        【解决方案10】:

        最简单的方法就是简单设置

        tableView.keyboardDismissMode = .onDrag
        

        这将开箱即用。

        【讨论】:

        • 对我根本不起作用。
        【解决方案11】:

        对于 Swift 5.0 并考虑 iPhone 预测文本已开启

        将 "keyboardSize.height + tableView.rowHeight" 作为 tableview 的底部,以防 iPhone Predictive Text 开启。在这种情况下,我们需要将 tableview 向上滚动一点。

            override func viewDidLoad() {
                super.viewDidLoad()
                
                tableView.delegate = self
                tableView.dataSource = self
                
                NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
                NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
            }
            
            @objc private func keyboardWillShow(notification: NSNotification) {
                if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
                    tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height + tableView.rowHeight, right: 0)
                }
            }
        
            @objc private func keyboardWillHide(notification: NSNotification) {
                tableView.contentInset = .zero
            }
        

        【讨论】:

        • 这对我来说似乎效果不佳。键盘第一次显示它可以工作,但第二次,keyboardSize.height 返回的值好像少了 100。
        猜你喜欢
        • 1970-01-01
        • 2018-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多