代码之家  ›  专栏  ›  技术社区  ›  abbood

使用“将单元格插入表格”时,UITableViewCell不使用自动布局高度

  •  2
  • abbood  · 技术社区  · 6 年前

    背景

    我使用pureLayout按照说明以编程方式创建uiTableViewCells here ,这基本上说明您必须在单元格上设置顶部/底部约束,然后使用

    self.tableView.rowHeight = UITableViewAutomaticDimension;
    

    要想让它正确:

    enter image description here

    问题

    除了在TableView中插入新行之外,其他一切都可以正常工作。我得到这个效果: https://youtu.be/eTGWsxwDAdk

    解释:只要我单击其中一个提示单元格,表就应该插入一个 驾驶员提示 行。不过,您会注意到,Wen I刷新部分(通过单击提示框),所有单元格的高度都会莫名其妙地增加,但是当我再次单击提示框时,它们会回到正常高度。 这是用这个代码完成的

    self.tableView.beginUpdates()
    self.tableView.reloadSections(IndexSet(integer:1), with: .automatic)
    self.tableView.endUpdates()
    

    这是cellfor行的实现

    // init table
    self.tableView.register(OrderChargeTableViewCell.self,
                                forCellReuseIdentifier: OrderChargeTableViewCell.regularCellIdentifier)
    self.tableView.register(OrderChargeTableViewCell.self,
                                forCellReuseIdentifier: OrderChargeTableViewCell.boldCellIdentifier)
    
    self.tableView.estimatedRowHeight = 25
    self.tableView.rowHeight = UITableViewAutomaticDimension
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        var cell: OrderChargeTableViewCell?
        if (indexPath.row == filteredModel.count-1) {
            cell = tableView.dequeueReusableCell(withIdentifier: OrderChargeTableViewCell.boldCellIdentifier,
                                                 for: indexPath) as? OrderChargeTableViewCell
        } else if (indexPath.row < filteredModel.count) {
            cell = tableView.dequeueReusableCell(withIdentifier: OrderChargeTableViewCell.regularCellIdentifier,
                                                 for: indexPath) as? OrderChargeTableViewCell
        }
    
        // add data to cell labels
        return cell!
    }
    

    这是uiTableViewCell本身的代码:

    最终类orderChargeTableViewCell:uiTableViewCell{

    // MARK: - init
    static let boldCellIdentifier = "TTOrderDetailBoldTableViewCell"
    static let regularCellIdentifier = "TTOrderDetailRegularTableViewCell"
    
    private var didSetupConstraints = false
    .. 
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    
        self.keyLabel = TTRLabel()
        self.valueLabel = TTRLabel()
    
        if (reuseIdentifier == OrderChargeTableViewCell.regularCellIdentifier) {
            self.isCellStyleBold = false
        } else if (reuseIdentifier == OrderChargeTableViewCell.boldCellIdentifier) {
            self.isCellStyleBold = true
        } else {
            self.isCellStyleBold = false
            assertionFailure( "Attempt to create an OrderCharge cell with the wrong cell identifier: \(String(describing: reuseIdentifier))")
        }
    
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    
    
        contentView.addSubview(keyLabel)
        contentView.addSubview(valueLabel)
    
    }
    
    override func updateConstraints()
    {
        if !didSetupConstraints {
            if (isCellStyleBold) {
                self.applyBoldFormatting()
            } else {
                self.applyRegularFormatting()
            }
    
            didSetupConstraints = true
        }
    
        super.updateConstraints()
    }
    public func applyBoldFormatting() {
        keyLabel.font = .ttrSubTitle
        valueLabel.font = .ttrBody
    
        keyLabel.autoPinEdge(.leading, to: .leading, of: contentView, withOffset: 15)
        keyLabel.autoAlignAxis(.vertical, toSameAxisOf: contentView)
    
        keyLabel.autoPinEdge(.top, to: .top, of: contentView, withOffset: 8)
        keyLabel.autoPinEdge(.bottom, to: .bottom, of: contentView, withOffset: -8)
    
        valueLabel.autoPinEdge(.trailing, to: .trailing, of: contentView, withOffset: -15)
        valueLabel.autoAlignAxis(.baseline, toSameAxisOf: keyLabel)
    }
    
    public func applyRegularFormatting() {
        keyLabel.font = .ttrCaptions
        valueLabel.font = TTRFont.Style.standard(.h3).value
    
        keyLabel.autoPinEdge(.leading, to: .leading, of: contentView, withOffset: 15)
        keyLabel.autoAlignAxis(.vertical, toSameAxisOf: contentView)
    
        keyLabel.autoPinEdge(.top, to: .top, of: contentView, withOffset: 6)
        keyLabel.autoPinEdge(.bottom, to: .bottom, of: contentView, withOffset: -4)
    
        valueLabel.autoPinEdge(.trailing, to: .trailing, of: contentView, withOffset: -15)
        valueLabel.autoAlignAxis(.baseline, toSameAxisOf: keyLabel)
    }
    

    插入的驱动程序提示行具有标准的44像素的单元格高度: enter image description here

    而其他(格式正确)单元格的高度为25: enter image description here

    2 回复  |  直到 6 年前
        1
  •  1
  •   DonMag    6 年前

    虽然您接下来的stackoverflow答案有很多上涨的选票,但似乎您采取了一个没有很好解释的要点(可能已经过时了),我认为这可能是导致您的问题的原因。

    你会发现很多评论/文章/文章说你应该在 updateConstraints() 然而, Apple's docs 也表示:

    重写此方法以优化对约束的更改。

    注释

    在发生影响的更改之后,几乎总是更干净、更容易立即更新约束。例如,如果要更改一个约束以响应按钮点击,请直接在按钮的操作方法中进行更改。

    只有当就地更改约束的速度太慢或视图产生大量冗余更改时,才应重写此方法。

    我认为如果添加子视图,您将在尝试的内容中获得更好的结果。 当你的手机初始化时,它们的约束。

    下面是一个简单的例子,它的布局与您所显示的类似。它创建了一个包含两个部分的表——第一个部分有一行,其中有一个“显示/隐藏”按钮。点击后,第二部分将添加/删除“驱动程序提示”行。

    //
    //  InsertRemoveViewController.swift
    //
    //  Created by Don Mag on 12/4/18.
    //
    
    import UIKit
    
    struct MyRowData {
        var title: String = ""
        var value: CGFloat = 0.0
    }
    
    class OrderChargeTableViewCell: UITableViewCell {
    
        static let boldCellIdentifier: String = "TTOrderDetailBoldTableViewCell"
        static let regularCellIdentifier: String = "TTOrderDetailRegularTableViewCell"
    
        var keyLabel: UILabel = {
            let v = UILabel()
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        var valueLabel: UILabel = {
            let v = UILabel()
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            commonInit()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }
    
        func commonInit() -> Void {
    
            contentView.addSubview(keyLabel)
            contentView.addSubview(valueLabel)
    
            let s = type(of: self).boldCellIdentifier
    
            if self.reuseIdentifier == s {
    
                NSLayoutConstraint.activate([
                    keyLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8.0),
                    keyLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8.0),
                    keyLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0),
    
                    valueLabel.centerYAnchor.constraint(equalTo: keyLabel.centerYAnchor, constant: 0.0),
                    valueLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15.0),
                    ])
    
                keyLabel.font = UIFont.systemFont(ofSize: 15, weight: .bold)
                valueLabel.font = UIFont.systemFont(ofSize: 15, weight: .bold)
    
            } else {
    
                NSLayoutConstraint.activate([
                    keyLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 6.0),
                    keyLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4.0),
                    keyLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0),
    
                    valueLabel.centerYAnchor.constraint(equalTo: keyLabel.centerYAnchor, constant: 0.0),
                    valueLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15.0),
                    ])
    
                keyLabel.font = UIFont.systemFont(ofSize: 12, weight: .bold)
                valueLabel.font = UIFont.systemFont(ofSize: 12, weight: .regular)
    
            }
    
        }
    
    }
    
    class TipCell: UITableViewCell {
    
        var callBack: (() -> ())?
    
        var theButton: UIButton = {
            let b = UIButton()
            b.translatesAutoresizingMaskIntoConstraints = false
            b.setTitle("Tap to Show/Hide Add Tip row", for: .normal)
            b.setTitleColor(.blue, for: .normal)
            b.backgroundColor = .yellow
            return b
        }()
    
        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            commonInit()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }
    
        func commonInit() -> Void {
    
            contentView.addSubview(theButton)
    
            NSLayoutConstraint.activate([
                theButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20.0),
                theButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20.0),
                theButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor, constant: 0.0),
                ])
    
            theButton.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
    
        }
    
        @objc func btnTapped(_ sender: Any?) -> Void {
            callBack?()
        }
    
    }
    
    class InsertRemoveViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
        var myData = [
            MyRowData(title: "SUBTOTAL", value: 4),
            MyRowData(title: "DELIVERY CHARGE", value: 1.99),
            MyRowData(title: "DISCOUNT", value: -1.99),
            MyRowData(title: "TOTAL", value: 4),
            ]
    
        var tableView: UITableView = {
            let v = UITableView()
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
    
        func tipRowShowHide() {
    
            let iPath = IndexPath(row: 3, section: 1)
    
            if myData.count == 4 {
                myData.insert(MyRowData(title: "DRIVER TIP", value: 2.0), at: 3)
                tableView.insertRows(at: [iPath], with: .automatic)
            } else {
                myData.remove(at: 3)
                tableView.deleteRows(at: [iPath], with: .automatic)
            }
    
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            tableView.register(OrderChargeTableViewCell.self,
                               forCellReuseIdentifier: OrderChargeTableViewCell.regularCellIdentifier)
            tableView.register(OrderChargeTableViewCell.self,
                               forCellReuseIdentifier: OrderChargeTableViewCell.boldCellIdentifier)
    
            tableView.register(TipCell.self, forCellReuseIdentifier: "TipCell")
    
            tableView.delegate = self
            tableView.dataSource = self
    
            tableView.rowHeight = UITableViewAutomaticDimension
            tableView.estimatedRowHeight = 25
    
            view.backgroundColor = .red
    
            view.addSubview(tableView)
    
            NSLayoutConstraint.activate([
                tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 200.0),
                tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20.0),
                tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
                tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),
                ])
    
        }
    
        func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
            return " "
        }
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return 2
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return section == 0 ? 1 : myData.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
            if indexPath.section == 0 {
    
                let cell = tableView.dequeueReusableCell(withIdentifier: "TipCell", for: indexPath) as! TipCell
    
                cell.callBack = {
                    self.tipRowShowHide()
                }
    
                return cell
    
            }
    
            var cell: OrderChargeTableViewCell?
    
            if indexPath.row == myData.count - 1 {
    
                cell = tableView.dequeueReusableCell(withIdentifier: OrderChargeTableViewCell.boldCellIdentifier,
                                                     for: indexPath) as? OrderChargeTableViewCell
    
            } else {
    
                cell = tableView.dequeueReusableCell(withIdentifier: OrderChargeTableViewCell.regularCellIdentifier,
                                                     for: indexPath) as? OrderChargeTableViewCell
    
            }
    
            cell?.keyLabel.text = myData[indexPath.row].title
    
            let val = myData[indexPath.row].value
            cell?.valueLabel.text = String(format: "%0.02f USD", val)
    
            return cell!
    
        }
    
    }
    

    结果是:

    enter image description here

        2
  •  0
  •   abbood    6 年前

    这是一个蛮力的解决方案,我一点也不为之骄傲,但它在这里仅供参考(不会标记为正确答案):

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let orderChargesSection = self.getOrderChargesSection()
        switch indexPath.section {
            case orderChargesSection:
                return self.getCellHeightForOrderCharges(row: indexPath.row)
            default:
                return UITableViewAutomaticDimension
        }
    }
    
    private func getCellHeightForOrderCharges(row: Int) -> CGFloat {
       let numRows = self.tableView(self.tableView, numberOfRowsInSection: self.getOrderChargesSection())
        if (row == numRows - 1) {
            return UITableViewAutomaticDimension
        } else {
            return 25.5
        }
    }