通过在 tableView 单元格(expand/collapse 单元格)中单击按钮显示隐藏标签

Show hidden label by button click inside tableView Cell (expand/collapse cell)

我有一个包含标签的 stackView(垂直),底部描述标签默认隐藏。我在单元格的右侧实现了一个箭头按钮。通过单击按钮,我只想显示隐藏的描述标签,stackView 应该会自动展开并使单元格变大。这是我实现可扩展单元的基本想法。 所以这是我用来获得所需结果的代码:

@objc func downArrowButtonClicked(_ sender: UIButton){
        let indexPath = IndexPath(row: sender.tag, section: 0)
        selectedIndex = indexPath
        selectedCellIndex = sender.tag
        isDescHidden = !isDescHidden
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }

以上是单击单元格内按钮的代码。我想到了重新加载该特定索引的想法。我创建了一个名为 selectedCellIndex 的变量,我在 cellForRowAt 方法中使用它来进行一些更改。

我还在 viewDidLayoutSubviews() 中实现了一些代码,因为当我第一次单击单元格时没有完全展开。以防万一:

 override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let indexPath = selectedIndex
        tableView.reloadRows(at: [indexPath ?? IndexPath(row: 0, section: 0)], with: .automatic)
    }

并在 willDisplay 方法中调用它,最终解决了单元格扩展问题:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        viewDidLayoutSubviews()
    }

这是我的 cellForRowAt 函数:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
            
            if indexPath.row == 0 {
                cell.lblTitle.text = "Title 1"
                cell.lblDesc.text = "Desc 1"
            }
            else if indexPath.row == 1 {
                cell.lblTitle.text = "Title 2"
                cell.lblDesc.text = "Desc 2"
            }
            else {
                cell.lblTitle.text = "Title 3"
                cell.lblDesc.text = "Desc 3"
            }
            
            if selectedCellIndex != nil {
                if isDescHidden == false {
                    if cell.isDescHidden == true {
                        cell.lblDesc.isHidden = false
                        cell.btnArrow.setImage(UIImage(systemName: "chevron.up"), for: .normal)
                    }
                    else {
                        cell.lblDesc.isHidden = true
                        cell.btnArrow.setImage(UIImage(systemName: "chevron.down"), for: .normal)
                    }
                }
                else {
                    if cell.isDescHidden == true {
                        cell.lblDesc.isHidden = true
                        cell.btnArrow.setImage(UIImage(systemName: "chevron.down"), for: .normal)
                    }
                    else {
                        cell.lblDesc.isHidden = false
                        cell.btnArrow.setImage(UIImage(systemName: "chevron.up"), for: .normal)
                    }
                }
                cell.isDescHidden = !cell.isDescHidden
            }
            
            cell.btnArrow.tag = indexPath.row
            cell.btnArrow.addTarget(self, action: #selector(downArrowButtonClicked(_:)), for: .touchUpInside)
            
            return cell
    }

从上面的代码可以看出,这种方法太混乱了。 isDescHidden 变量在主视图控制器和 table 视图单元格 class 中都有定义,我试图同时使用它们来展开或折叠特定的单元格。但是第一次它工作但如果我有 3 个单元格展开,折叠按钮单击不起作用 1-2 单击然后工作。

对于这种问题有更好的方法吗?或者有什么方法可以直接从 @objc func downArrowButtonClicked(_ sender: UIButton) 函数设置 cell.isDescHidden 值?所以我可以在 cellForRowAt 函数中使用它吗? 如果我可以直接从中更改单元格变量,我会很高兴。

使用以下函数自动设置行高,并为您的 stackView

提供 topbottom 约束
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }

这是我的 CustomCell

class CustomCell: UITableViewCell {
    @IBOutlet weak var titleCell: UILabel!
    @IBOutlet weak var detail: UILabel!
    @IBOutlet weak var arrowButton: UIButton!

    let upArrow = UIImage(systemName: "chevron.up.circle.fill")
    let downArrow = UIImage(systemName: "chevron.down.circle.fill")
    var onArrowClick: ((UIButton)->())!
    
    @IBAction func handleArrowButton(sender: UIButton){
        onArrowClick(sender)
    }

    func updateArrowImage(expandStatus: Bool){
        arrowButton.setImage(expandStatus ? downArrow : upArrow, for: .normal)
    }
}

示例数据

   let data = [
        ["Nothing", "description is very long description is very long description is very long description is very  "],
        ["Nothing", "description is very long "],
        ["Nothing", "description is very long "],
        ["Nothing", "description is very long "],
        ["Nothing", "description is very long "]
    ]

var eachCellStatus: [Bool] = []

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
     super.viewDidLoad()
     tableView.dataSource = self
     tableView.delegate = self
     for _ in data {
         eachCellStatus.append(true)
     }
}

TableView 方法是这样的

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
    
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomCell
        cell.titleCell.text = data[indexPath.row][0]
        cell.detail.text = data[indexPath.row][1]
        cell.detail.isHidden = eachCellStatus[indexPath.row]
        cell.updateArrowImage(expandStatus: self.eachCellStatus[indexPath.row])
        cell.onArrowClick = { button in
            self.eachCellStatus[indexPath.row].toggle()
            cell.updateArrowImage(expandStatus: self.eachCellStatus[indexPath.row])
            tableView.reloadRows(at: [indexPath], with: .automatic)
        }
        return cell
    }

首先创建一个模型来将数据显示到单元格中。你必须保留细胞的状态。

struct CellData {
    var title: String
    var details: String
    var isExpanded: Bool
}

CustomTableViewCell 中为 cellData 添加一个 属性 并从中分配 Outlets 数据。同时创建一个 protocol 以从 UIViewController

重新加载 row
protocol CustomTableViewCellDelegate {
    func reloadRow(sender: CustomTableViewCell, flag: Bool)
}

class CustomTableViewCell: UITableViewCell {
    
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var detailsLabel: UILabel!
    @IBOutlet weak var showButton: UIButton!
    
    var indexPath: IndexPath?
    var delegate: CustomTableViewCellDelegate?
    
    var data: CellData? {
        didSet {
            
            if let data = data {
                if data.isExpanded == false {
                    detailsLabel.isHidden = true
                    showButton.setImage(UIImage(systemName: "chevron.down"), for: .normal)
                    
                }else {
                    detailsLabel.isHidden = true
                    showButton.setImage(UIImage(systemName: "chevron.up"), for: .normal)
                }
                
                titleLabel.text = data.title
                detailsLabel.text = data.details
            }
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        titleLabel.text = nil
        detailsLabel.text = nil
    }
    
    @IBAction func showButtonAction(_ sender: UIButton) {
        if var data = data {
            data.isExpanded.toggle()
            delegate?.reloadRow(cell: self, flag: data.isExpanded)
        }
    }
}

UIViewController中添加一个CellData类型的数组。您可以在 viewDidLoad() 方法中分配它的数据。

var tableData: [CellData]

修改 numberOfRowsInSection()cellForRow() 方法,如 bleow。

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
    cell.data = tableData[indexPath.row]
    cell.delegate = self
    return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

然后确认 CustomTableViewCellDelegate 协议到 UIViewController

extension ViewController: CustomTableViewCellDelegate {
    func reloadRow(sender: CustomTableViewCell, isExpanded: Bool) {
        guard let tappedIndexPath = tableView.indexPath(for: sender) else { return }
        tableData[tappedIndexPath.row].isExpanded = isExpanded
        tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.automatic)
    }
}