ios - 从自定义 Cell 中正确委托(delegate)按钮操作以删除 UITableView 中的行

标签 ios swift uitableview swift3 swift-protocols

仍然是一个 Swift 菜鸟,我一直在寻找一种正确的方法/最佳实践来管理我的 UITableView(使用自定义 UserCell)中的行删除基于使用委托(delegate)在 UserCell 中点击 UIButton,这似乎是最简洁的方法。

我按照这个例子:UITableViewCell Buttons with action

我有什么

UserCell 类

protocol UserCellDelegate {

    func didPressButton(_ tag: Int)
}

class UserCell: UITableViewCell {

    var delegate: UserCellDelegate?
    let addButton: UIButton = {

        let button = UIButton(type: .system)

        button.setTitle("Add +", for: .normal)
        button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

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

        addSubview(addButton)
        addButton.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -6).isActive = true
        addButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
        addButton.heightAnchor.constraint(equalToConstant: self.frame.height / 2).isActive = true
        addButton.widthAnchor.constraint(equalToConstant: self.frame.width / 6).isActive = true
    }

    func buttonPressed(_ sender: UIButton) {

        delegate?.didPressButton(sender.tag)
    }
}

TableViewController类:

class AddFriendsScreenController: UITableViewController, UserCellDelegate {

    let cellId = "cellId"
    var users = [User]()

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return users.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! UserCell

        cell.delegate = self
        cell.tag = indexPath.row

        return cell
    }

    func didPressButton(_ tag: Int) {

        let indexPath = IndexPath(row: tag, section: 0)

        users.remove(at: tag)
        tableView.deleteRows(at: [indexPath], with: .fade)
    }
}

users 中的 User 附加了对 View Controller 中数据库的调用。

我的问题

  • Table View 每一行的按钮都是可点击的,但不执行任何操作
  • 该按钮似乎只有在进行“长按”时才可点击,即手指在其上停留约 0.5 秒
  • 此方法是否保证 indexPath 已更新并且不会超出范围? IE。如果在索引 0 处删除一行,删除索引 0 处的"new"行会正常工作还是会删除索引 1 处的行?

我想要什么

能够单击表格每一行中的按钮,这会将其从表格 View 中删除。

我一定是犯了一些相当基本的错误,如果有 Swift 骑士能启发我,我将不胜感激。

非常感谢。

最佳答案

您的代码中至少有 3 个问题:

  • UserCell 中你应该调用:
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)

一旦你的单元格被实例化(比如,从你的 init(style:reuseIdentifier:) 的实现),这样 self 就会引用 的实际实例用户单元格

  • AddFriendsScreenControllertableView(_:cellForRowAt:) 中,您正在设置单元格本身的标签(cell.tag = indexPath.row) 但在你的 UserCellbuttonPressed(_:) 中你使用的是按钮的标签。您应该将该函数修改为:
func buttonPressed(_ sender: UIButton) {

    //delegate?.didPressButton(sender.tag)
    delegate?.didPressButton(self.tag)
}
  • 如您所料并根据 Prema Janoti's answer您应该在删除一行后重新加载表格 View ,因为您的单元格标签将与其引用的 indexPaths 不同步。理想情况下,您应该避免依赖索引路径来识别单元格,但这是另一个主题。

编辑:
避免标签与索引路径不同步的一个简单解决方案是将每个单元格与它们应该表示的 User 对象相关联:

  • 首先将 user 属性添加到您的 UserCell 类:
class UserCell: UITableViewCell {

    var user = User()   // default with a dummy user

    /* (...) */
}
  • 将此属性设置为 tableView(_:cellForRowAt:) 中正确的 User 对象:
//cell.tag = indexPath.row
cell.user = self.users[indexPath.row]
  • 修改您的 UserCellDelegate 协议(protocol)方法的签名,以传递存储在单元格中的 user 属性,而不是它的 tag:
protocol UserCellDelegate {

    //func didPressButton(_ tag: Int)
    func didPressButtonFor(_ user: User)

}
  • 相应地修改 UserCellbuttonPressed(_:) 操作:
func buttonPressed(_ sender: UIButton) {

    //delegate?.didPressButton(sender.tag)
    //delegate?.didPressButton(self.tag)
    delegate?.didPressButtonFor(self.user)
}
  • 最后,在您的 AddFriendsScreenController 中,根据数据源中的 User 位置确定要删除的正确行:
//func didPressButton(_ tag: Int) { /* (...) */ }   // Scrap this.

func didPressButtonFor(_ user: User) {

    if let index = users.index(where: { $0 === user }) {

        let indexPath = IndexPath(row: index, section: 0)

        users.remove(at: index)
        tableView.deleteRows(at: [indexPath], with: .fade)
    }
}

注意 if let index = ... 构造 ( optional binding ) 和三元组 === ( identity operator )。

这种方法的缺点是它会在您的 UserUserCell 类之间建立紧密耦合。最佳做法是使用更复杂的 MVVM pattern例如,但这确实是另一个主题......

关于ios - 从自定义 Cell 中正确委托(delegate)按钮操作以删除 UITableView 中的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42226291/

相关文章:

ios - 我如何使用 dynamicType 在 swift3 中执行 "perform(selector:)"

iOS Autolayout 自适应 UI 问题

ios - iOS 10 中的 AVPlayerItemFailedToPlayToEndTime 在哪里?

ios - 声明我的 CellClass 以扩展到 UITableViewCell?

ios - 如何删除 UITableView 中的行并更新 UITableViewCell 中的标签

ios - 在静态库中使用 ALAssetsLibrary 时无法枚举组/结果

ios - 自定义 UIView 未接收触摸事件

iOS API 引用历史

ios - 在 View 之间传递图像

ios - GCD 混淆了滚动 TableView 上的数据