如何以编程方式使用动态高度扩展和折叠tableview单元格?

时间:2019-01-31 02:45:40

标签: ios swift uitableview tableview

以下是我的问题的视频:https://imgur.com/a/qvGSLfD

我的自定义单元格有2个标签:一个主标签和一个副标题标签,这两个标签都在各个方向上都限制在该单元格的contentView layoutMarginsGuide中。

首先,整个扩展折叠机制似乎有些笨拙,因为我先激活和停用约束,然后重新加载indexPath。

我尝试过这种方法:当我选择单元格时,我停用了只设置一个标签的约束并激活了两个标签的约束,而当它折叠时,我则相反。我觉得这不是一个好方法,但是我找不到能够支持动态像元高度的任何东西。

基本上,我可以做这样的事情:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath.row == selectedRowIndex {
        return 200 //Expanded
    }
    return tableView.rowHeight //Not expanded
}

但是对于大小不同的单元格,我不能只返回相同的数字。

这是我的viewController类中的tableView代码:

 var tableView: UITableView = {
    let tv = UITableView(frame: .zero)
    tv.register(CustomCell.self, forCellReuseIdentifier: CustomCell.reuseIdentifier())
    tv.translatesAutoresizingMaskIntoConstraints = false
    tv.rowHeight = UITableView.automaticDimension
    tv.estimatedRowHeight = 60.0
    tv.estimatedSectionHeaderHeight = 0
    tv.estimatedSectionFooterHeight = 0
    tv.showsVerticalScrollIndicator = false
    tv.tableFooterView = UIView()
    tv.alwaysBounceVertical = true
    tv.decelerationRate = .fast
    tv.bounces = false
    tv.dataSource = self
    tv.delegate = self
    return tv
}()

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: CustomCell.reuseIdentifier(), for: indexPath) as! CustomCell
    cell.bounds = CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 99999)
    cell.contentView.bounds = cell.bounds
    cell.layoutIfNeeded()
    cell.wordLabel.preferredMaxLayoutWidth = cell.wordLabel.frame.width
    cell.descriptionLabel.preferredMaxLayoutWidth = cell.descriptionLabel.frame.width

    //customize this later
    cell.backgroundColor = .white
    cell.set(content: datasource[indexPath.row])
    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath) as! CustomCell
    cell.toggle()
    tableView.reloadRows(at: [indexPath], with: .automatic)
}

以下是相关的自定义单元格功能:

var isExpanded: Bool!

private var singleLabelConstraints: [NSLayoutConstraint]!
private var doubleLabelConstraints: [NSLayoutConstraint]!

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    setupLabels()
}

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

func toggle() {
    isExpanded = !isExpanded
    if isExpanded == false {
        print("collapsed")
        self.descriptionLabel.isHidden = true
        NSLayoutConstraint.activate(singleLabelConstraints)
        NSLayoutConstraint.deactivate(doubleLabelConstraints)
    } else if isExpanded == true {
        print("expanded")
        self.descriptionLabel.isHidden = false
        NSLayoutConstraint.deactivate(singleLabelConstraints)
        NSLayoutConstraint.activate(doubleLabelConstraints)
    }
}

func setupLabels() {
    isExpanded = false
    descriptionLabel.isHidden = true
    self.contentView.addSubview(wordLabel)
    self.contentView.addSubview(descriptionLabel)

    singleLabelConstraints = [
        wordLabel.leadingAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.leadingAnchor,
            constant: labelInsets.left
        ),
        wordLabel.topAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.topAnchor,
            constant: labelInsets.top
        ),
        wordLabel.trailingAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.trailingAnchor,
            constant: labelInsets.right
        ),
        wordLabel.bottomAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.bottomAnchor,
            constant: labelInsets.bottom
        )
    ]

    doubleLabelConstraints = [
        wordLabel.leadingAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.leadingAnchor,
            constant: labelInsets.left
        ),
        wordLabel.topAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.topAnchor,
            constant: labelInsets.top
        ),
        wordLabel.trailingAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.trailingAnchor,
            constant: labelInsets.right
        ),

        descriptionLabel.leadingAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.leadingAnchor,
            constant: labelInsets.left
        ),
        descriptionLabel.topAnchor.constraint(
            equalTo: self.wordLabel.bottomAnchor,
            constant: labelInsets.top
        ),
        descriptionLabel.trailingAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.trailingAnchor,
            constant: labelInsets.right
        ),
        descriptionLabel.bottomAnchor.constraint(
            equalTo: self.contentView.layoutMarginsGuide.bottomAnchor,
            constant: labelInsets.bottom
        )
    ]
    NSLayoutConstraint.activate(singleLabelConstraints)
}

我希望过渡更加顺畅,我希望可以轻松切换单元格同时将主标签保持在原位置并仅显示字幕标签。

3 个答案:

答案 0 :(得分:1)

您可以使用UIStackView来展开和折叠表格视图。选择表格视图单元格后,您可以隐藏和显示描述标签。

class ViewController: UIViewController {
    var tableView: UITableView = {
        let tv = UITableView(frame: .zero)
        tv.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
        tv.translatesAutoresizingMaskIntoConstraints = false
        tv.rowHeight = UITableView.automaticDimension
        tv.estimatedRowHeight = 100.0
        tv.estimatedSectionHeaderHeight = 0
        tv.estimatedSectionFooterHeight = 0
        tv.showsVerticalScrollIndicator = false
        tv.tableFooterView = UIView()
        tv.alwaysBounceVertical = true
        tv.decelerationRate = .fast
        tv.bounces = false
        return tv
    }()
    var selectedCell:IndexPath?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(tableView)
        tableView.dataSource = self
        tableView.delegate = self
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[tableView]|", options: [], metrics: nil, views: ["tableView":tableView]))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[tableView]|", options: [], metrics: nil, views: ["tableView":tableView]))
    }


}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as? CustomCell ?? CustomCell()
        if let selectedCell = selectedCell, selectedCell == indexPath {
            cell.descriptionLabel.isHidden = false
        } else {
            cell.descriptionLabel.isHidden = true
        }
        return cell
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedCell = indexPath
        tableView.reloadData()
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }

}

class CustomCell: UITableViewCell {

    let stackView = UIStackView()
    let wordLabel = UILabel()
    let descriptionLabel = UILabel()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupLabels()
    }

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

    func setupLabels() {

        selectionStyle = .none

        stackView.axis = .vertical
        stackView.distribution = .equalSpacing
        stackView.spacing = 5
        stackView.alignment = .fill
        stackView.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(stackView)

        wordLabel.translatesAutoresizingMaskIntoConstraints = false
        wordLabel.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lor"
        wordLabel.numberOfLines = 0
        wordLabel.lineBreakMode = .byWordWrapping
        stackView.addArrangedSubview(wordLabel)

        descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
        descriptionLabel.text = "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
        descriptionLabel.numberOfLines = 0
        descriptionLabel.lineBreakMode = .byWordWrapping
        stackView.addArrangedSubview(descriptionLabel)

        wordLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 30).isActive = true
        descriptionLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 30).isActive = true

        stackView.leadingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.leadingAnchor,constant: 10).isActive = true
        stackView.topAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.topAnchor,constant: 10).isActive = true
        stackView.trailingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.trailingAnchor,constant: 10).isActive = true
        stackView.bottomAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.bottomAnchor,constant: 10).isActive = true

    }
}

答案 1 :(得分:0)

尝试一下:

tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: .automatic)
tableView.endUpdates()

答案 2 :(得分:0)

您好,您可以通过下面的链接

解决问题

http://vasundharavision.com/blog/ios/expandable-tableview-without-third-party

我希望它对您有用。