簡體   English   中英

iOS 11+ 的可擴展自定義 UITableViewCell

[英]Expandable custom UITableViewCell for iOS 11+

我知道很多人在各種forms中都問過這個問題,答案遍布整個頁面,所以讓我總結一下我的具體情況,希望得到更具體的答案。 首先,我正在為 iOS 11+ 構建,並且擁有相對較新的 XCode (11+) 版本。 也許不是最新的,但足夠近了。

基本上,我需要一個自定大小的表格視圖,當用戶與它們交互時,單元格可能會在運行時展開和折疊。 viewDidLoad中,我將 rowHeight 設置為UITableView.automaticDimension並將estimatedRowHeight 設置為大於預設值 44 的某個數字。但單元格並沒有像應有的那樣擴展,即使我似乎已經嘗試了書中的每一個建議。

如果這很重要,我有一個用於表格單元格的自定義 class 但沒有用於它的.XIB 文件 - UI 直接在原型中定義。 我嘗試了許多其他變體,但感覺最簡單的方法是讓 UIStackView 成為原型的唯一直接子項(可以說“收入”功能都在其中。就我而言,它們包括 label和另一個tableview - 我嵌套3層深 - 但這可能不是重點)並將它的所有4個邊緣約束到父級。 我已經嘗試過了,我已經修改了堆棧視圖中的分布(填充、均勻填充、按比例填充),但似乎都不起作用。 我該怎么做才能使細胞正常膨脹?

萬一有人想知道,我曾經覆蓋heightForRowAt但現在我沒有,因為在運行時預測高度並不容易,我希望這個過程可以自動化。

從基礎開始...

這是一個帶有兩個標簽的垂直UIStackView

在此處輸入圖像描述

紅色輪廓顯示堆棧視圖的框架。

如果我們點擊按鈕,它將設置bottomLabel.isHidden = true

在此處輸入圖像描述

請注意,除了被隱藏之外,堆棧視圖還會刪除它所占用的空間。

現在,我們可以在表格視圖單元格中使用堆棧視圖來實現展開/折疊功能。

我們將從擴展的每隔一行開始:

在此處輸入圖像描述

現在我們點擊第 1 行的“折疊”按鈕,我們得到:

在此處輸入圖像描述

不完全是我們想要的。 我們成功地“折疊”了單元格內容,但表格視圖對此一無所知。

所以,我們可以添加一個閉包......當我們點擊按鈕時,單元格中的代碼將顯示/隱藏底部 label並且它將使用閉包告訴表格視圖發生了什么。 我們的cellForRowAt函數如下所示:

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

    c.setData("Top \(indexPath.row)", str2: "Bottom \(indexPath.row)\n2\n3\n4\n5", isCollapsed: isCollapsedArray[indexPath.row])

    c.didChangeHeight = { [weak self] isCollapsed in
        guard let self = self else { return }
        // update our data source
        self.isCollapsedArray[indexPath.row] = isCollapsed
        // tell the tableView to re-run its layout
        self.tableView.performBatchUpdates(nil, completion: nil)
    }

    return c
}

我們得到:

在此處輸入圖像描述

這是一個完整的例子:

簡單的“虛線輪廓視圖”

class DashedOutlineView: UIView {
    
    @IBInspectable var dashColor: UIColor = .red
    var shapeLayer: CAShapeLayer!
    
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        shapeLayer = self.layer as? CAShapeLayer
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.lineDashPattern = [8,8]
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        shapeLayer.strokeColor = dashColor.cgColor
        shapeLayer.path = UIBezierPath(rect: bounds).cgPath
    }
}

電池 class

class ExpColCell: UITableViewCell {

    public var didChangeHeight: ((Bool) -> ())?
    
    private let stack = UIStackView()
    private let topLabel = UILabel()
    private let botLabel = UILabel()
    private let toggleButton = UIButton()
    
    private let outlineView = DashedOutlineView()
    
    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 {
        // button properties
        toggleButton.translatesAutoresizingMaskIntoConstraints = false
        toggleButton.backgroundColor = .systemBlue
        toggleButton.setTitleColor(.white, for: .normal)
        toggleButton.setTitleColor(.gray, for: .highlighted)
        toggleButton.setTitle("Collapse", for: [])
        
        // label properties
        topLabel.text = "Top Label"
        botLabel.text = "Bottom Label"
        topLabel.font = .systemFont(ofSize: 32.0)
        botLabel.font = .italicSystemFont(ofSize: 24.0)
        topLabel.backgroundColor = .green
        botLabel.backgroundColor = .systemTeal
        
        botLabel.numberOfLines = 0
        
        // outline view properties
        outlineView.translatesAutoresizingMaskIntoConstraints = false
        
        // stack view properties
        stack.translatesAutoresizingMaskIntoConstraints = false
        stack.axis = .vertical
        stack.spacing = 8
        
        // add the labels
        stack.addArrangedSubview(topLabel)
        stack.addArrangedSubview(botLabel)
        
        // add outlineView, stack view and button to contentView
        contentView.addSubview(outlineView)
        contentView.addSubview(stack)
        contentView.addSubview(toggleButton)
        
        // we'll use the margin guide
        let g = contentView.layoutMarginsGuide
        
        NSLayoutConstraint.activate([
            
            stack.topAnchor.constraint(equalTo: g.topAnchor),
            stack.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            
            outlineView.topAnchor.constraint(equalTo: stack.topAnchor),
            outlineView.leadingAnchor.constraint(equalTo: stack.leadingAnchor),
            outlineView.trailingAnchor.constraint(equalTo: stack.trailingAnchor),
            outlineView.bottomAnchor.constraint(equalTo: stack.bottomAnchor),

            toggleButton.topAnchor.constraint(equalTo: g.topAnchor),
            toggleButton.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            toggleButton.leadingAnchor.constraint(equalTo: stack.trailingAnchor, constant: 16.0),
            toggleButton.widthAnchor.constraint(equalToConstant: 92.0),
            
        ])
        
        // we set the bottomAnchor constraint like this to avoid intermediary auto-layout warnings
        let c = stack.bottomAnchor.constraint(equalTo: g.bottomAnchor)
        c.priority = UILayoutPriority(rawValue: 999)
        c.isActive = true

        // set label Hugging and Compression to prevent them from squeezing/stretching
        topLabel.setContentHuggingPriority(.required, for: .vertical)
        topLabel.setContentCompressionResistancePriority(.required, for: .vertical)
        botLabel.setContentHuggingPriority(.required, for: .vertical)
        botLabel.setContentCompressionResistancePriority(.required, for: .vertical)

        contentView.clipsToBounds = true
        
        toggleButton.addTarget(self, action: #selector(toggleButtonTapped), for: .touchUpInside)
        
    }
    
    func setData(_ str1: String, str2: String, isCollapsed: Bool) -> Void {
        topLabel.text = str1
        botLabel.text = str2
        botLabel.isHidden = isCollapsed
        updateButtonTitle()
    }
    func updateButtonTitle() -> Void {
        let t = botLabel.isHidden ? "Expand" : "Collapse"
        toggleButton.setTitle(t, for: [])
    }
    
    @objc func toggleButtonTapped() -> Void {
        botLabel.isHidden.toggle()
        updateButtonTitle()
        
        // comment / un-comment this line to see the difference
        didChangeHeight?(botLabel.isHidden)
    }
}

和一個表格視圖 controller 來演示

class ExpColTableViewController: UITableViewController {

    var isCollapsedArray: [Bool] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.register(ExpColCell.self, forCellReuseIdentifier: "c")
        
        // 16 "rows" start with every-other row collapsed
        for i in 0..<15 {
            isCollapsedArray.append(i % 2 == 0)
        }
        
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return isCollapsedArray.count
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! ExpColCell

        c.setData("Top \(indexPath.row)", str2: "Bottom \(indexPath.row)\n2\n3\n4\n5", isCollapsed: isCollapsedArray[indexPath.row])

        c.didChangeHeight = { [weak self] isCollapsed in
            guard let self = self else { return }
            // update our data source
            self.isCollapsedArray[indexPath.row] = isCollapsed
            // tell the tableView to re-run its layout
            self.tableView.performBatchUpdates(nil, completion: nil)
        }

        return c
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM