表格视图单元格选中标记在滚动时更改位置

时间:2017-09-21 19:05:43

标签: ios swift uitableview scroll

我是Swift的新手。我在tableview中创建了一个简单的列表。当用户长按一行时,将检查该行。它工作得非常好。但是当我向下滚动时,勾选标记会改变它的位置。我还尝试在NSMutableSet中存储位置。但它仍然无法正常工作。也许我做错了什么。

这是我的代码:

长时间调用此方法。

func longpress(gestureRecognizer: UIGestureRecognizer)
{
    let longpress = gestureRecognizer as! UILongPressGestureRecognizer
    let state = longpress.state
    let locationInview = longpress.location(in: tableview1)
    var indexpath=tableview1.indexPathForRow(at: locationInview)

    if(gestureRecognizer.state == UIGestureRecognizerState.began)
    {
        if(tableview1.cellForRow(at: indexpath!)?.accessoryType == 
        UITableViewCellAccessoryType.checkmark)
        {
            tableview1.cellForRow(at: indexpath!)?.accessoryType = 
                UITableViewCellAccessoryType.none
        }
        else{
            tableview1.cellForRow(at: indexpath!)?.accessoryType = 
                UITableViewCellAccessoryType.checkmark
        }
    }
}

2 个答案:

答案 0 :(得分:1)

问题是细胞会被重复使用,当您更新复选标记时,您需要更新单元格,但不会更新模型。因此,当单元格滚出视图并重复使用单元格时,cellForRowAt显然不会重置表格新行的复选标记。

同样,如果您将单元格滚动回视图,cellForRowAt无法知道是否应该检查单元格。你必须

  • 当您在单元格上检测到手势时,您必须更新模型才能知道此行的单元格应该有一个检查;以及

  • 配置单元格时,cellForRowAt必须查看此属性。

因此,首先要确保您的模型具有一些值,以指示是否已选中/选择它。在此示例中,我将使用" Item",但您使用更有意义的类型名称:

struct Item {
    let name: String
    var checked: Bool
}

然后您的视图控制器可以在cellForRowAt

中正确填充单元格
class ViewController: UITableViewController {

    var items: [Item]!

    override func viewDidLoad() {
        super.viewDidLoad()

        addItems()
    }

    /// Create a lot of sample data so I have enough for a scrolling view

    private func addItems() {
        let formatter = NumberFormatter()
        formatter.numberStyle = .spellOut
        items = (0 ..< 1000).map { Item(name: formatter.string(from: NSNumber(value: $0))!, checked: false) }
    }
}

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

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
        cell.delegate = self
        cell.textLabel?.text = items[indexPath.row].name
        cell.accessoryType = items[indexPath.row].checked ? .checkmark : .none
        return cell
    }
}

现在,我通常让单元格处理诸如识别手势之类的内容并相应地通知视图控制器。因此,创建一个UITableViewCell子类,并将其指定为故事板上单元格原型中的基类。但是该单元需要一些协议来通知视图控制器发生了长时间的压力:

protocol ItemCellDelegate: class {
    func didLongPressCell(_ cell: UITableViewCell)
}

表视图控制器可以处理这个委托方法,切换其模型并相应地重新加载单元格:

extension ViewController: ItemCellDelegate {
    func didLongPressCell(_ cell: UITableViewCell) {
        guard let indexPath = tableView.indexPath(for: cell) else { return }

        items[indexPath.row].checked = !items[indexPath.row].checked
        tableView.reloadRows(at: [indexPath], with: .fade)
    }
}

然后,UITableViewCell子类只需要一个长按手势识别器,并在识别出手势后通知视图控制器:

class ItemCell: UITableViewCell {

    weak var delegate: CellDelegate?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
        addGestureRecognizer(longPress)
    }

    @IBAction func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
        if gesture.state == .began {
            delegate?.didLongPressCell(self)
        }
    }
}

顺便说一句,通过在单元格上设置手势,可以避免由于&#34;由于我长时间按下不属于单元格的内容而导致的混淆。单元格是手势识别器的正确位置。

答案 1 :(得分:0)

您没有在任何地方存储更改。

为避免使用太多内存,手机会重复使用单元格,并要求您在TableView的{​​{1}}中配置它们。

假设你有一个名为dataSource的数组,它有一些结构可以存储你想要显示为单元格的内容。您需要更新此数组并告诉tableView重新加载您的单元格。

data

在配置单元格时始终设置附件:

func userDidLongPress(gestureRecognizer: UILongPressGestureRecognizer) {

    // We only care if the user began the longPress
    guard gestureRecognizer.state == UIGestureRecognizerState.began else {
        return
    }

    let locationInView = gestureRecognizer.location(in: tableView)

    // Nothing to do here if user didn't longPress on a cell
    guard let indexPath = tableView.indexPathForRow(at: locationInView) else {
        return
    }

    // Flip the associated value and reload the row
    data[indexPath.row].isChecked = !data[indexPath.row].isChecked
    tableView.reloadRows(at: [indexPath], with: .automatic)
}