ios - 如何以编程方式展开和折叠具有动态高度的表格 View 单元格?

标签 ios swift uitableview tableview

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

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

首先,整个展开折叠机制看起来有点笨拙,因为我要激活和停用约束,然后重新加载 indexPaths。

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

基本上,我可以这样做:

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

以下是相关的自定义 Cell 函数:

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

我希望过渡更顺畅,我想要一些可以轻松切换单元格的东西,同时保持主标签就位,然后只显示副标题标签。

最佳答案

您可以使用 UIStackView用于展开和折叠表格 View 。您可以在选择表格 View 单元格时隐藏和显示描述标签。

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

    }
}

关于ios - 如何以编程方式展开和折叠具有动态高度的表格 View 单元格?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54452589/

相关文章:

ios - 为什么 UIScrollView 在移动到另一个文本字段时会重新调整大小 - iOS iPhone

ios - iOS 15 设备在测试应用程序内购买期间忽略我的沙盒帐户

ios - 为什么引用 Bool 会发出警告

ios - 使用可选值解码嵌套 JSON Swift 4

ios - 如何在 ios 中单击时更改 tableView Cell 按钮图像

ios - 无法将类型 'UITableViewController' 的值转换为 'UINavigationController'

ios - 如何在 Swift 中使用只读属性来符合 Objective-C 协议(protocol)

ios - 如何快速隐藏谷歌地图标记

ios - UIPanGestureRecognizer左右tableviewcell

ios - UITableView - 自定义 UITableViewCell 中的自定义 selectedBackgroundView 在选择时隐藏单元格分隔符