当键盘显示时,Tableview 滚动内容

41

我有一个表格视图,带有一个文本字段和一个文本视图。我已按照苹果的示例代码建议实现了这段代码 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)

}
当我点击滚动视图的“文本”时,它会到达屏幕顶部之上,但当我释放键盘时,它仍然保持向上滚动。就像插入属性在第一次之后不能被修改一样。我的错误在哪里?
12个回答

69
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)
    }
}

这是最好的答案。谢谢。 - Ariel Antonio Fundora
4
请移除通知观察器。 - Gajendra Rawat
4
如果你先关闭键盘再重新打开,可能会导致错误的高度值。为了避免这种情况发生,只需使用UIKeyboardFrameEndUserInfoKey而不是UIKeyboardFrameBeginUserInfoKey即可。 - ooxio
1
最佳答案,我也可以建议取消通知,请。deinit { NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) } - captain_haddock
请注意,不再需要删除观察者:https://dev59.com/sV4b5IYBdhLWcg3wxURi#40339926 - nickdnk

30

适用于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
}

3
这很棒,只需要添加滚动到行的功能即可。 - Ben Shabat
let indexPath:IndexPath = IndexPath(row: #, section: #) tableView.scrollToRow(at: indexPath, at: .bottom, animated: true) - Kurt L.
我也尝试了同样的方法,但我的文本框没有上移。 - Aman Gupta

21

尝试保留编辑索引路径 editingIndexPath 获取索引路径,并将表格滚动到该索引路径处。

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构建的,也许有某些特殊选项? - msalafia
@Andorath,你能给我传一下样例吗? - Yatheesha
我该如何向您发送样本? - msalafia
我也遇到了同样的问题。有人能提供建议吗? - Nupur Gupta
请查看此链接。我希望它能对您有所帮助。https://code.tutsplus.com/tutorials/ios-sdk-keeping-content-from-underneath-the-keyboard--mobile-6103 - Disha

21

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

将“keyboardSize.height + tableView.rowHeight”视为tableview的底部,以防万一iPhone预测文本开启。在这种情况下,我们需要向上滚动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
    }

1
这对我来说似乎表现不佳。第一次键盘显示时它可以工作,但第二次,keyboardSize.height返回的值少了大约100。 - paul_f
@paul_f 这是因为 keyboardFrameBeginUserInfoKey。要获取“正确”的(最终)高度,请使用 keyboardFrameEndUserInfoKey。 - Thel

20

使用这个很棒的扩展(已更新至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
    }
}

并按照下面各自的视图控制器使用,

@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并展示一个使用块的例子。谢谢。 - Peter Kreinz
1
更新了Swift 4.2的答案。 - Soumen
extension UIScrollView 之前缺少一个 }(且缩进略有偏差)。 - John J. Camilleri
1
非常棒的扩展,但是否还有其他人注意到底部边缘插图的值不是很正确?我的视图和键盘之间会有额外的死空间(请参见此处)。该视图已对齐到安全区域。我必须使用函数 return height - (height > 200 ? 50 : 30) 调整 keyboardSize.height 的值。 - John J. Camilleri
1
请注意删除观察者,否则可能会出现内存泄漏。参见:https://developer.apple.com/library/archive/releasenotes/Foundation/RN-FoundationOlderNotes/index.html#:~:text=Block%20based%20observers,is%20still%20supported. - CyberMew
太好了!也许还需要考虑安全区域边距。 - deeJ

12

使用以下代码获取IndexPath,并根据UIKeyboard的高度更改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
    }
}

7
最简单的方法是直接设置。
tableView.keyboardDismissMode = .onDrag

这将可以直接使用。


完全不适用于我。 - paul_f
1
这只是在拖动时取消了键盘,但对内容插图没有影响。 - Davyd Geyl

4

**键盘弹出时不向上滚动**

    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的答案相同。 - yoninja

2
我有一个小想法,然后我用 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
    }
}

2
您也可以使用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部分,因为它不是必需的。这对我有效。分享一些你的代码。 - DisplayName

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接