可扩展的TableViewCells - 重用单元格问题

时间:2017-01-21 09:39:39

标签: ios swift uitableview tableviewcell

我创建了可扩展的UITableViewCell类,它修改了constaint以实现效果。

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有点不对。

enter image description here

修改

Sulthan 回答给了我很多帮助,但滚动到顶部的问题尤其是在点击最低的单元格时仍然存在。

尝试了这个UITableView scrolls to top when reloading cells with changing cell heights

使用estimatedRowHeight到最接近的值但是在可扩展单元格的情况下它没有解决我的问题。

有人知道如何解决它吗?

1 个答案:

答案 0 :(得分:1)

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

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()
    }
}