首先,我是一个相当新的Swift,我一直在寻找一个很好的解决方案来处理UITableView中不同自定义单元格的键盘上的Previous,Next和Done按钮。我已经查看了Stack Overflow上的各种解决方案,但它们都不能满足我的需求。
我的tableview每行有一个字段(UITextField,UITextView等),需要通用的方法从一个单元格移动到另一个单元格。一些解决方案不能解释下一个小区可能在屏幕外的情况。
我已经提出了一个解决方案,我将其作为答案发布。如果您有任何建议,请随时评论改进方法!
答案 0 :(得分:1)
请检查此库。简单有效。您只需要通过appDelegate中的可可豆荚和单行代码安装
pod 'IQKeyboardManagerSwift'
https://github.com/hackiftekhar/IQKeyboardManager
在App代理
中IQKeyboardManager.sharedManager().enable = true
答案 1 :(得分:0)
对于我的自定义单元格,我有一个基类作为基础,因为我以编程方式创建所有内容。它看起来像这样:
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) {}
}
我使用纯粹快速机制的扩展程序,以便在我们不需要这些按钮的情况下使上一个和下一个实现可选。
然后,在我的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上使用insets处理键盘节目并隐藏通知:
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
这让我接受了大量的反复试验,以确保我的视图控制器具有应该在单元类中的代码。
我一直在寻找更好的方法来做到这一点。让我知道你的想法!