如何通过单击根据其内容更改表格单元格高度(折叠和展开)? [英] How to change table cell height (collapse and expand) according to its content by clicking?

查看:28
本文介绍了如何通过单击根据其内容更改表格单元格高度(折叠和展开)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最初用户看到的单元格是这样的(只有黑色区域.描述被隐藏).也就是说,一个单元格在描述之前是可见的.

Initially the user sees cell like this (Only black area. Description is hidden). That is, a cell is visible up to description.

我想在点击单元格后,它的高度增加到单元格的末尾,就像这样.

I want after clicking on the cell that its height increase to the end of the cell, like this.

标题和描述都不是静态的.它们的大小取决于内容.

Both Title and Description are not static. Their size depends on the content.

在这种情况下,您可以看到我总是将高度更改为恒定值.这不符合我的要求.

You can see in this case I always change the height to constant values. It's not good for my requirements.

extension MyTableView: UITableViewDataSource, UITableViewDelegate {

    //another funcs...

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

    func numberOfSections(in tableView: UITableView) -> Int {
        return myDataArray.count
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if selectedRowIndex == indexPath.section {
            return 150  // I want the full cell size to be returned here (cell expanded)
        } else {
            return 75  // and here size returned only up to specific view (cell collapsed)
        }
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.beginUpdates()
        if selectedRowIndex == indexPath.section {
            selectedRowIndex = -1
        } else {
            selectedRowIndex = indexPath.section
        }
        tableView.endUpdates()
    }

    //another funcs...
}

推荐答案

有多种实现可扩展"单元格的方法——这一种可能非常适合您的设计需求...

There are various approaches to "expandable" cells - this one may work well for your design needs...

获得自定大小单元格的常用方法是确保您有一个干净的自上而下的链"约束:

The common way to get self-sizing cells is by making sure you have a clean "top-to-bottom chain" of constraints:

使用此布局,橙色视图对黑色视图(其超级视图)的底部有 8 磅的约束.

With this layout, the orange view has an 8-pt constraint to the bottom of the black view (its superview).

为了使这个单元格可展开/可折叠,我们可以添加另一个 8-pt约束,这次是从蓝色视图的底部到黑色视图的底部.

To make this cell expandable / collapsible, we can add another 8-pt constraint, this time from the bottom of the blue view to the bottom of the black view.

最初,我们会遇到约束冲突,因为黑色视图的底部不能与蓝色视图相距 8-pts 与橙色视图相距 8-pts同时.

Initially, we'll have constraint conflicts, because the bottom of the black view cannot be 8-pts from the blue view and 8-pts from the orange view at the same time.

所以,我们给他们不同的优先级...

So, we give them different priorities...

如果我们给 "blue-bottom" 约束一个 .defaultHigh (750) 的优先级,给 "orange-bottom" 约束一个 .defaultLow (250) 的优先级,我们告诉自动布局强制具有较高优先级的约束并允许打破较低优先级的约束,我们得到:

If we give "blue-bottom" constraint a Priority of .defaultHigh (750) and the "orange-bottom" constraint a Priority of .defaultLow (250), we're telling auto-layout to enforce the constraint with the higher priority and allow the lower priority constraint to break, and we get this:

橙色视图仍然存在,但它现在在黑色视图的边界之外,所以我们看不到它.

The orange view is still there, but it is now outside the bounds of the black view, so we don't see it.

这是一个非常简单的例子...

Here is a very simple example...

我们为单元格配置了两个底部约束 - 一个来自标题标签视图的底部,一个来自描述标签视图的底部.

We configure the cell with two Bottom constraints - one from the bottom of the Title Label View and one from the bottom of the Description Label View.

我们为每个约束设置高优先级或低优先级,具体取决于我们希望单元格展开还是折叠.

We set high or low priority on each constraint, depending on whether we want the cell expanded or collapsed.

点击一行将切换其展开状态.

Tapping on a row will toggle its expanded state.

这一切都是通过代码完成的 - 没有 @IBOutlet@IBAction 连接 - 所以只需添加一个新的 UITableViewController 并将其类分配给TestTableViewController:

This is all done via code - no @IBOutlet or @IBAction connections - so just add a new UITableViewController and assign its class to TestTableViewController:

class MyExpandableCell: UITableViewCell {

    let myImageView: UIImageView = {
        let v = UIImageView()
        v.backgroundColor = UIColor(red: 219.0 / 255.0, green: 59.0 / 255.0, blue: 38.0 / 255.0, alpha: 1.0)
        v.contentMode = .scaleAspectFit
        v.tintColor = .white
        v.layer.cornerRadius = 16.0
        return v
    }()

    let myTitleView: UIView = {
        let v = UIView()
        v.backgroundColor = UIColor(red: 68.0 / 255.0, green: 161.0 / 255.0, blue: 247.0 / 255.0, alpha: 1.0)
        v.layer.cornerRadius = 16.0
        return v
    }()

    let myDescView: UIView = {
        let v = UIView()
        v.backgroundColor = UIColor(red: 243.0 / 255.0, green: 176.0 / 255.0, blue: 61.0 / 255.0, alpha: 1.0)
        v.layer.cornerRadius = 16.0
        return v
    }()

    let myTitleLabel: UILabel = {
        let v = UILabel()
        v.numberOfLines = 0
        v.textAlignment = .center
        v.textColor = .white
        return v
    }()

    let myDescLabel: UILabel = {
        let v = UILabel()
        v.numberOfLines = 0
        v.textColor = .white
        return v
    }()

    let myContainerView: UIView = {
        let v = UIView()
        v.clipsToBounds = true
        v.backgroundColor = .black
        return v
    }()

    var isExpanded: Bool = false {
        didSet {
            expandedConstraint.priority = isExpanded ? .defaultHigh : .defaultLow
            collapsedConstraint.priority = isExpanded ? .defaultLow : .defaultHigh
        }
    }

    var collapsedConstraint: NSLayoutConstraint!
    var expandedConstraint: NSLayoutConstraint!

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    func commonInit() -> Void {

        [myImageView, myTitleView, myDescView, myTitleLabel, myDescLabel, myContainerView].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
        }

        myTitleView.addSubview(myTitleLabel)
        myDescView.addSubview(myDescLabel)
        myContainerView.addSubview(myTitleView)
        myContainerView.addSubview(myDescView)
        myContainerView.addSubview(myImageView)
        contentView.addSubview(myContainerView)

        let g = contentView.layoutMarginsGuide

        expandedConstraint = myDescView.bottomAnchor.constraint(equalTo: myContainerView.bottomAnchor, constant: -8.0)
        collapsedConstraint = myTitleView.bottomAnchor.constraint(equalTo: myContainerView.bottomAnchor, constant: -8.0)

        expandedConstraint.priority = .defaultLow
        collapsedConstraint.priority = .defaultHigh

        NSLayoutConstraint.activate([

            myTitleLabel.topAnchor.constraint(equalTo: myTitleView.topAnchor, constant: 12.0),
            myTitleLabel.leadingAnchor.constraint(equalTo: myTitleView.leadingAnchor, constant: 8.0),
            myTitleLabel.trailingAnchor.constraint(equalTo: myTitleView.trailingAnchor, constant: -8.0),
            myTitleLabel.bottomAnchor.constraint(equalTo: myTitleView.bottomAnchor, constant: -12.0),

            myDescLabel.topAnchor.constraint(equalTo: myDescView.topAnchor, constant: 12.0),
            myDescLabel.leadingAnchor.constraint(equalTo: myDescView.leadingAnchor, constant: 8.0),
            myDescLabel.trailingAnchor.constraint(equalTo: myDescView.trailingAnchor, constant: -8.0),
            myDescLabel.bottomAnchor.constraint(equalTo: myDescView.bottomAnchor, constant: -12.0),

            myImageView.topAnchor.constraint(equalTo: myContainerView.topAnchor, constant: 8.0),
            myImageView.leadingAnchor.constraint(equalTo: myContainerView.leadingAnchor, constant: 8.0),
            myImageView.trailingAnchor.constraint(equalTo: myContainerView.trailingAnchor, constant: -8.0),

            myImageView.heightAnchor.constraint(equalToConstant: 80),

            myTitleView.topAnchor.constraint(equalTo: myImageView.bottomAnchor, constant: 8.0),
            myTitleView.leadingAnchor.constraint(equalTo: myContainerView.leadingAnchor, constant: 8.0),
            myTitleView.trailingAnchor.constraint(equalTo: myContainerView.trailingAnchor, constant: -8.0),

            myDescView.topAnchor.constraint(equalTo: myTitleView.bottomAnchor, constant: 8.0),
            myDescView.leadingAnchor.constraint(equalTo: myContainerView.leadingAnchor, constant: 8.0),
            myDescView.trailingAnchor.constraint(equalTo: myContainerView.trailingAnchor, constant: -8.0),

            myContainerView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            myContainerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            myContainerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            myContainerView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),

            expandedConstraint, collapsedConstraint,
        ])


    }

}

class TestTableViewController: UITableViewController {

    let myData: [[String]] = [
        ["Label", "A label can contain an arbitrary amount of text, but UILabel may shrink, wrap, or truncate the text, depending on the size of the bounding rectangle and properties you set. You can control the font, text color, alignment, highlighting, and shadowing of the text in the label."],
        ["Button", "You can set the title, image, and other appearance properties of a button. In addition, you can specify a different appearance for each button state."],
        ["Segmented Control", "The segments can represent single or multiple selection, or a list of commands.\n\nEach segment can display text or an image, but not both."],
        ["Text Field", "Displays a rounded rectangle that can contain editable text. When a user taps a text field, a keyboard appears; when a user taps Return in the keyboard, the keyboard disappears and the text field can handle the input in an application-specific way. UITextField supports overlay views to display additional information, such as a bookmarks icon. UITextField also provides a clear text control a user taps to erase the contents of the text field."],
        ["Slider", "UISlider displays a horizontal bar, called a track, that represents a range of values. The current value is shown by the position of an indicator, or thumb. A user selects a value by sliding the thumb along the track. You can customize the appearance of both the track and the thumb."],
        ["This cell has a TItle that will wrap onto multiple lines.", "Just to demonstrate that auto-layout is handling text wrapping in the title view."],
    ]

    var rowState: [Bool] = [Bool]()

    override func viewDidLoad() {
        super.viewDidLoad()

        // initialize rowState array to all False (not expanded
        rowState = Array(repeating: false, count: myData.count)

        tableView.register(MyExpandableCell.self, forCellReuseIdentifier: "cell")
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

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

        cell.myImageView.image = UIImage(systemName: "\(indexPath.row).circle")
        cell.myTitleLabel.text = myData[indexPath.row][0]
        cell.myDescLabel.text = myData[indexPath.row][1]
        cell.isExpanded = rowState[indexPath.row]

        cell.selectionStyle = .none

        return cell
    }

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    guard let c = tableView.cellForRow(at: indexPath) as? MyExpandableCell else {
        return
    }
    rowState[indexPath.row].toggle()
    tableView.performBatchUpdates({
        c.isExpanded = rowState[indexPath.row]
    }, completion: nil)
}

}

结果:

然后,点击并滚动一下:

and, after tapping and scrolling a bit:

这篇关于如何通过单击根据其内容更改表格单元格高度(折叠和展开)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆