跨 UITableView 自定义单元格处理键盘的上一个、下一个和完成按钮

Posted

技术标签:

【中文标题】跨 UITableView 自定义单元格处理键盘的上一个、下一个和完成按钮【英文标题】:Handling Previous, Next and Done buttons for keyboard across UITableView custom cells 【发布时间】:2018-03-23 17:09:17 【问题描述】:

首先,我是 Swift 的新手,我一直在寻找一个好的解决方案来处理 UITableView 中不同自定义单元格的键盘上的 Previous、Next 和 Done 按钮。我查看了 Stack Overflow 上的各种解决方案,但没有一个能 100% 满足我的需要。

我的 tableview 每行有一个字段(UITextField、UITextView 等),需要一种通用的方式从一个单元格移动到下一个单元格。一些解决方案没有考虑下一个单元格可能不在屏幕上的情况。

我想出了一个解决方案,我将作为答案发布。如果您有任何建议,请随时评论改进方法!

【问题讨论】:

【参考方案1】:

请检查此库。简单有效。您只需要通过可可豆荚和 appDelegate 中的单行代码进行安装

pod 'IQKeyboardManagerSwift'

https://github.com/hackiftekhar/IQKeyboardManager

应用内委托

IQKeyboardManager.sharedManager().enable = true

【讨论】:

【参考方案2】:

对于我的自定义单元格,我有一个基类作为基础,因为我以编程方式创建所有内容。它看起来像这样:

class BaseTableViewCell: UITableViewCell 
    weak var delegate: BaseTableViewCellDelegate? = nil
    var indexPath: IndexPath? = nil

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupViews()
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    func setupViews() 
        fatalError("BaseTableViewCell setupViews not overridden")
    

    func handleBecomeFirstResponser() 
        // handle in derived class
    

    func handleResignFirstResponder() 
        // handle in derived class
    

另外,我有一个代表这个基类的委托,如下所示:

protocol BaseTableViewCellDelegate: class 
    func cellEdited(indexPath: IndexPath)
    func cellPreviousPressed(indexPath: IndexPath)
    func cellNextPressed(indexPath: IndexPath)
    func cellNeedsResize(indexPath: IndexPath)


// Using extension to provide default implementation for previous/next actions
//   This makes then optional for a cell that doesn't need them
extension BaseTableViewCellDelegate 
    func cellPreviousPressed(indexPath: IndexPath) 
    func cellNextPressed(indexPath: IndexPath) 
    func cellNeedsResize(indexPath: IndexPath) 

我正在使用纯 swift 机制的扩展来使上一个和下一个实现可选,以防我们不需要这些按钮。

然后,在我的 BaseTableViewCell 类中,我有一个函数可以像这样设置键盘工具栏(如下)。我还有另一个函数来支持 UITextView(可能有更好的方法来做到这一点;不确定)。

func setupKeyboardToolbar(targetTextField: UITextField, dismissable: Bool, previousAction: Bool, nextAction: Bool) 
    let toolbar: UIToolbar = UIToolbar()
    toolbar.sizeToFit()

    var items = [UIBarButtonItem]()
        let previousButton = UIBarButtonItem(image: UIImage(imageLiteralResourceName: "previousArrowIcon"), style: .plain, target: nil, action: nil)
        previousButton.width = 30
        if !previousAction 
            previousButton.isEnabled = false
         else 
            previousButton.target = self
            previousButton.action = #selector(toolbarPreviousPressed)
        

        let nextButton = UIBarButtonItem(image: UIImage(imageLiteralResourceName: "nextArrowIcon"), style: .plain, target: nil, action: nil)
        nextButton.width = 30
        if !nextAction 
            nextButton.isEnabled = false
         else 
            nextButton.target = self
            nextButton.action = #selector(toolbarNextPressed)
        

        items.append(contentsOf: [previousButton, nextButton])

    let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissKeyboard))
    items.append(contentsOf: [spacer, doneButton])

    toolbar.setItems(items, animated: false)
    targetTextField.inputAccessoryView = toolbar

以下是相关的动作例程:

func toolbarPreviousPressed() 
    if delegate != nil && indexPath != nil 
        delegate?.cellPreviousPressed(indexPath: indexPath!)
    


func toolbarNextPressed() 
    if delegate != nil && indexPath != nil 
        delegate?.cellNextPressed(indexPath: indexPath!)
    

在我拥有 tableview 的视图控制器中,我的 cellForRowAt 函数具有以下代码:

    let cell = tableView.dequeueReusableCell(withIdentifier: "textFieldCell") as! TextFieldCell
    let addressItem = (item as! XXXXXXAddressViewModelItem)
    cell.textField.placeholder = addressItem.placeHolderText
    cell.textField.text = addressItem.getValue(row: 0)
    cell.indexPath = indexPath
    cell.delegate = self
    cell.setupKeyboardToolbar(targetTextField: cell.textField, dismissable: true, previousAction: false, nextAction: true)
    return cell

这是我如何处理按下的上一个和下一个按钮的委托方法:

func cellPreviousPressed(indexPath: IndexPath) 
    // Resign the old cell
    let oldCell = tableView.cellForRow(at: indexPath) as! BaseTableViewCell
    oldCell.handleResignFirstResponder()

    // Scroll to previous cell
    let tempIndex = IndexPath(row: indexPath.row, section: indexPath.section - 1)
    tableView.scrollToRow(at: tempIndex, at: .middle, animated: true)

    // Become first responder
    let cell = tableView.cellForRow(at: tempIndex) as! BaseTableViewCell
    cell.handleBecomeFirstResponser()


func cellNextPressed(indexPath: IndexPath) 
    // Resign the old cell
    let oldCell = tableView.cellForRow(at: indexPath) as! BaseTableViewCell
    oldCell.handleResignFirstResponder()

    // Scroll to next cell
    let tempIndex = IndexPath(row: indexPath.row, section: indexPath.section + 1)
    self.tableView.scrollToRow(at: tempIndex, at: .middle, animated: true)

    // Become first responder for new cell
    let cell = self.tableView.cellForRow(at: tempIndex) as! BaseTableViewCell
    cell.handleBecomeFirstResponser()

最后,在从 BaseTableViewCell 派生的单元类中,我像这样覆盖 handleBecomeFirstResponder 和 handleResignFirstResponder:

override func handleBecomeFirstResponder() 
    textField.becomeFirstResponder()


override func handleResignFirstResponder() 
    textField.resignFirstResponder()

在相关说明中,我使用 tableview 上的插图处理键盘显示和隐藏通知:

self.tableView.contentInset = UIEdgeInsetsMake(0, 0, (keyboardFrame?.height)!, 0)
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, (keyboardFrame?.height)!, 0)

和:

self.tableView.contentInset = UIEdgeInsets.zero
self.tableView.scrollIndicatorInsets = UIEdgeInsets.zero

这花了我大量的试验和错误来解决这个问题,并且没有严重地将我的视图控制器与应该在单元类中的代码混淆。

我一直在寻找更好的方法来做到这一点。让我知道你的想法!

【讨论】:

以上是关于跨 UITableView 自定义单元格处理键盘的上一个、下一个和完成按钮的主要内容,如果未能解决你的问题,请参考以下文章

将自定义 UITableViewCell 滚动到键盘上方?

在 UITableView 中处理自定义单元格的点击不会发生

将文本输入到包含 UITextFields 的 UITableView 单元格时的键盘控制

带有单个自定义单元格的 UITableView? (苹果手机)

如何自定义uitableview

使用自定义键盘时使 UITableView 滚动到正确的位置