可扩展的 TableView 单元格 - 使用单元格问题

Expandable TableViewCells - reusing cells issue

我创建了可扩展的 UITableViewCell class,它修改了 containt 以达到效果。

import UIKit

class ExpandableCell: UITableViewCell {

@IBOutlet weak var img: UIImageView!


@IBOutlet weak var imgHeightConstraint: NSLayoutConstraint!


var isExpanded:Bool = false
    {
    didSet
    {
        if !isExpanded {
            self.imgHeightConstraint.constant = 0.0

        } else {
            self.imgHeightConstraint.constant = 128.0
        }
    }
}

}

视图控制器

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 21
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell:ExpandableCell = tableView.dequeueReusableCell(withIdentifier: "ExpandableCell") as! ExpandableCell
    cell.img.image = UIImage(named: imgs[indexPath.row])
    cell.isExpanded = false
    return cell
}

// TableView Delegate methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    print(indexPath.row)
    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
        else { return }

        UIView.animate(withDuration: 0.3, animations: {
            let offset: CGPoint = self.tableView.contentOffset

            tableView.beginUpdates()
            cell.isExpanded = !cell.isExpanded

            self.tableView.layer.removeAllAnimations()
            self.tableView.setContentOffset(offset, animated: false)
            tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.top, animated: true)
            tableView.endUpdates()



        })

    }

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
        else { return }
    UIView.animate(withDuration: 0.3, animations: {
        tableView.beginUpdates()
        cell.isExpanded = false
        tableView.endUpdates()
    })
}

前几行一切正常,向下滚动后,单元格行为不正常,似乎滚动到顶部,然后图像消失了。

我知道这可能与未在某些数组中存储布尔值 isExpanded 以跟踪它有关,但我认为刷新 TableView 有问题。

编辑

Sulthan 回答对我帮助很大,但滚动到顶部的问题一直存在,尤其是在点击最低单元格时。

试过这个

将 estimatedRowHeight 设置为最接近的值,但在可扩展单元格的情况下它不能解决我的问题。

有人知道如何修复它吗?

首先将展开的单元格保存在控制器中:

var expandedRows = Set<Int>()

使用它们的人:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell:ExpandableCell = tableView.dequeueReusableCell(withIdentifier: "ExpandableCell") as! ExpandableCell
    cell.img.image = UIImage(named: imgs[indexPath.row])

    // this!
    cell.isExpanded = self.expandedRows.contains(indexPath.row)
    return cell
}

并且在更改展开状态时:

if self.expandedRows.contains(indexPath.row) {
    self.expandedRows.remove(indexPath.row)
    cell.isExpanded = false
} else {
    self.expandedRows.add(indexPath.row)
    cell.isExpanded = true
}

当然还有另一种可能。由于您的单元格在被选中时会展开,因此您只需对单元格中的事件做出反应即可:

class ExpandableCell : UITableViewCell {
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        self.imgHeightConstraint.constant = selected ? 128.0 : 0.0
    }
}

完整示例代码:

class ExpandableCell : UITableViewCell {
    var label: UILabel?
    var expandableView: UIView?
    var heightConstraint: NSLayoutConstraint?

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

        self.initViews()
    }

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

        self.initViews()
    }

    private func initViews() {
        self.contentView.clipsToBounds = true

        let label = UILabel()
        label.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]
        label.frame = CGRect(x: 0, y: 0, width: self.contentView.frame.size.width, height: 44)

        self.label = label
        self.contentView.addSubview(label)

        let expandableView = UIView()
        expandableView.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(expandableView)

        let horizontalConstraints = NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-0-[view]-0-|", options: [], metrics: nil, views: ["view": expandableView]
        )
        NSLayoutConstraint.activate(horizontalConstraints)

        let verticalConstraints = NSLayoutConstraint.constraints(
            withVisualFormat: "V:|-44-[view]-0@999-|", options: [], metrics: nil, views: ["view": expandableView]
        )
        NSLayoutConstraint.activate(verticalConstraints)

        let heightConstraint = NSLayoutConstraint(
            item: expandableView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0
        )
        NSLayoutConstraint.activate([heightConstraint])

        self.expandableView = expandableView
        self.heightConstraint = heightConstraint
    }

    var isExpanded:Bool = false {
        didSet {
            self.heightConstraint?.constant = self.isExpanded ? 128 : 0
        }
    }
}

class ViewController: UITableViewController {
    var expandedRows: Set<Int> = Set()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(ExpandableCell.self, forCellReuseIdentifier: "ExpandableCell")
        self.tableView.rowHeight = UITableViewAutomaticDimension
        self.tableView.allowsMultipleSelection = false
    }

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

    override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 44.0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandableCell", for: indexPath) as! ExpandableCell

        cell.label!.text = "Cell at row: \(indexPath.row)"
        cell.isExpanded = expandedRows.contains(indexPath.row)
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Selecting: \(indexPath.row)")
        self.expandedRows.insert(indexPath.row)

        if let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell {
            cell.isExpanded = true
        }

        self.tableView.beginUpdates()
        self.tableView.endUpdates()
    }

    override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        print("Deselecting: \(indexPath.row)")
        self.expandedRows.remove(indexPath.row)

        if let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell {
            cell.isExpanded = false
        }

        self.tableView.beginUpdates()
        self.tableView.endUpdates()
    }
}