仍然是一个 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
就会引用 的实际实例用户单元格
。
- 在
AddFriendsScreenController
的tableView(_:cellForRowAt:)
中,您正在设置单元格本身的标签(cell.tag = indexPath.row
) 但在你的UserCell
的buttonPressed(_:)
中你使用的是按钮的标签。您应该将该函数修改为:
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)
}
- 相应地修改
UserCell
的buttonPressed(_:)
操作:
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 )。
这种方法的缺点是它会在您的 User
和 UserCell
类之间建立紧密耦合。最佳做法是使用更复杂的 MVVM pattern例如,但这确实是另一个主题......
关于ios - 从自定义 Cell 中正确委托(delegate)按钮操作以删除 UITableView 中的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42226291/