使用将单元格插入表格时,UITableViewCell 不使用自动布局高度 [英] UITableViewCell doesn't use autolayout height when using insert cell into table

查看:55
本文介绍了使用将单元格插入表格时,UITableViewCell 不使用自动布局高度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我使用 purelayout 以编程方式创建我的 UITableViewCells,按照

问题

一切正常,除非我在 tableView 中插入新行.我得到这个效果:

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

解决方案

虽然您关注的 StackOverflow 答案有很多赞成票,但您似乎选择了一个没有很好解释的要点(可能已经过时),我认为这可能是导致您出现问题的原因.

您会发现许多评论/帖子/文章指出您应该在 updateConstraints() 中添加约束,但是,

Background

I use purelayout to programmatically create my UITableViewCells, following the instructions here, which basically states that you gotta set the top/bottom constraints on a cell, then use

self.tableView.rowHeight = UITableViewAutomaticDimension;

to get it right:

Problem

Everything works fine, except when I insert a new row into a tableView. I get this effect: https://youtu.be/eTGWsxwDAdk

To explain: as soon as I click on one of the tip cells, the table is supposed to insert a driver tip row. However you'll notice that wen i refresh the section (by clicking on a tip box), all the cells height inexplicably increases, but when i click on tip boxes again, they go back to their normal height this is done with this code

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

this is the implementation of the cellfor row

// 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!
}

and this is the code for the UITableViewCell itself:

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

the driver tip row that gets inserted has the standard 44 pixel height of a cell:

whereas the other (properly formatted) cells have the 25 height:

解决方案

While the StackOverflow answer you followed has a lot of up-votes, it appears you took one bullet point which was not very well explained (and may be outdated), and I think that may be what's causing your problems.

You'll find many comments / posts / articles stating you should add your constraints in updateConstraints(), however, Apple's docs also state:

Override this method to optimize changes to your constraints.

Note

It is almost always cleaner and easier to update a constraint immediately after the affecting change has occurred. For example, if you want to change a constraint in response to a button tap, make that change directly in the button’s action method.

You should only override this method when changing constraints in place is too slow, or when a view is producing a number of redundant changes.

I think you'll get much better results in what you're attempting if you add your subviews and their constraints when your cell is init'd.

Here is a simple example which has a similar layout to what you've shown. It creates a table with 2 sections - first section has a row with a "show/hide" button. When tapped, the second section will add/remove the "Driver Tip" row.

//
//  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!

    }

}

This is the result:

这篇关于使用将单元格插入表格时,UITableViewCell 不使用自动布局高度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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