Высота UITableViewCell, динамически рассчитанная с помощью автоматического размещения на основе содержимого строки - PullRequest
0 голосов
/ 17 ноября 2018

Я подготовил UITableViewCell в nib-файле со всеми ограничениями сверху вниз. Расчет высоты строки в табличном представлении, кажется, работает.

Но если я хочу активировать / деактивировать некоторые другие ограничения в ячейке на основе некоторого условия данных строки, то у меня есть проблема, потому что кажется, что высота не рассчитывается тогда. В чем проблема? В следующем примере TableRowDM.hidden означает, должны ли субтитры быть скрытыми или нет. Я скрываю эту UILabel, активируя не активированное ограничение (высота с константой = 0) и устанавливая вертикальный разрыв между titleLabel и subtitleLabel равным 0.

struct TableRowDM {
    let title: String
    let subTitle: String
    let hidden: Bool
}

let cellId: String = "CellView"

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    let rowData: [TableRowDM] = [
        TableRowDM(title: "Row 1", subTitle: "Short title 1th row", hidden: false),
        TableRowDM(title: "Row 2", subTitle: "Short title 2th row", hidden: true),
        TableRowDM(title: "Row 3", subTitle: "Very long text in subtitle at 3th row to test text wrapping and growing subtitle height", hidden: false),
        TableRowDM(title: "Row 4", subTitle: "Long text in subtitle at 4th row", hidden: false),
        TableRowDM(title: "Row 5", subTitle: "Long text in subtitle at 5th row", hidden: true),
        TableRowDM(title: "Row 6", subTitle: "Long text in subtitle at 6th row", hidden: false),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 50
        tableView.tableFooterView = UIView(frame: CGRect.zero)
        tableView.register(UINib(nibName: cellId, bundle: nil), forCellReuseIdentifier: cellId)
        tableView.delegate = self
        tableView.dataSource = self
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CellView
        let row: TableRowDM = self.rowData[indexPath.row]

        cell.title.text = row.title
        cell.subtitle.text = row.subTitle

        if row.hidden {
            // hide subtitle
            cell.subtitleHeight.isActive = true
            cell.titleToSubtitleGap.constant = 0
        } else {
            // show subtitle
            cell.subtitleHeight.isActive = false
            cell.titleToSubtitleGap.constant = 16
        }
        cell.setNeedsLayout()
        cell.layoutIfNeeded()
        return cell
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return rowData.count
    }
}

Кажется, что это не работает, и subtitleLabel не скрывается, а высота строки всегда одинакова. Когда содержимое subtitleLabel и titleLabel изменяется, высота корректируется в соответствии с текстом внутри этих UIViews, но когда я пытаюсь манипулировать с ограничениями, это не работает. Любая помощь? Что я делаю неправильно? CellView - это очень простой вид с такими подпредставлениями

-----------------
      |
- titleLabel ----
      |
      | <- titleToSubtitleGap
      |
- subtitleLabel -
      |
-----------------

1 Ответ

0 голосов
/ 18 ноября 2018

Я нашел решение для моего вопроса.Это нигде не задокументировано, но работает.

Что важно.Если вы поддерживаете альбомную / портретную ориентации, важно установить правильные приоритеты в NSLayoutConstraints, который определяет вертикальную непрерывную цепочку ограничений и представлений (с определенной высотой), как указано здесь - они написали:

Далее, расположите содержимое ячейки табличного представления в представлении содержимого ячейки.Чтобы определить высоту ячейки, вам нужна непрерывная цепочка ограничений и представлений (с определенной высотой), чтобы заполнить область между верхним краем представления содержимого и его нижним краем.Если ваши представления имеют внутреннюю высоту содержимого, система использует эти значения.Если нет, то вы должны добавить соответствующие ограничения по высоте, либо к представлениям, либо к самому представлению содержимого.

Они не написали, что все эти вертикальные ограничения, которые окончательно определяютВысота ячейки таблицы contentView должна иметь приоритеты, меньшие, чем требуется (1000), если необходимо поддерживать множество ориентаций устройства.Я установил в своих примерах эти приоритеты на Высокий (750).

Далее важно внести изменения с этими ограничениями в методе updateConstraints.Так что, если вы проектируете UIView и когда какое-либо свойство этого представления должно изменить ограничения, вам нужно сделать это примерно так в своем представлении, чтобы метод updateConstraints был вызван в нужный момент.После установки свойства новое значение вызывается needsUpdateConstraints(), чтобы AutoLayout знал, что ограничения в нашем UIView изменились.

import UIKit

class ListItemTableViewCell: UITableViewCell {

    @IBOutlet weak var subtitleToTitleGap: NSLayoutConstraint!
    @IBOutlet weak var subtitleHeightZero: NSLayoutConstraint!

    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var subtitleLabel: UILabel!

    public var isSubtitleHidden: Bool {
        get {
            return self.subtitleLabel.isHidden
        }
        set {
            // Here we are changing property that change layout in our view
            self.subtitleLabel.isHidden = newValue
            self.needsUpdateConstraints() // Here we are telling AutoLayout to recalculate constraints in `updateConstraints` method.
        }
    }

#if !TARGET_INTERFACE_BUILDER
    // We need to block this code from running in Interface Builder
    override func updateConstraints() {
        if self.isSubtitleHidden {
            // Hide Subtitle
            self.subtitleToTitleGap.constant = 0.0
            self.subtitleHeightZero.autoInstall() // This makes subtitle height to be 0
        } else {
            // Show Subtitle
            self.subtitleToTitleGap.constant = 2.0
            self.subtitleHeightZero.autoRemove() // This will make subtitle height to be self calculated
        }
        super.updateConstraints() // don't forget to call this after You finished constraints changes
    }
#endif
}

И самое важное, и это было самым трудным для меня выяснить, это скрыть реализациюupdateConstraints от конструктора интерфейса, потому что он периодически вызывал сбой IB в редакторе xib.Посмотрите выше для этого примера.Метод заключен в макрос препроцессора #if.Так что я думаю, вот и все.Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...