Swift 4.2 TableViewCell динамическая высота программно - PullRequest
0 голосов
/ 15 марта 2019

Это не дублированный вопрос, потому что нет реального решения этой проблемы

Я пытаюсь реализовать UITableViewcell динамическую высоту по содержимому с использованием ограничений, но получаю предупреждение о компоновке:

Попытается восстановиться, нарушив ограничение

Создайте символическую точку останова в UIViewAlertForUnsatisfiableConstraints чтобы поймать это в отладчике. Методы в UIConstraintBasedLayoutDebugging категории на UIView, перечисленные в также может быть полезным 2019-03-15 12: 27: 52.085475 + 0400 TableCellDynamicHeight [31984: 1295380] [LayoutConstraints] Невозможно одновременно удовлетворить ограничения. Вероятно, по крайней мере одно из ограничений в следующем списке ты не хочешь Попробуйте это: (1) посмотреть на каждое ограничение и попытаться выяснить, чего вы не ожидаете; (2) найти код, который добавил нежелательное ограничение или ограничения и исправление. ( "", "", "", "")

Я проверил некоторые темы: Высота динамического tableViewCell

Проблема динамической высоты для ячеек UITableView (Swift)

Swift 3 - Пользовательская динамическая высота TableViewCell - программно

Что такое правильное решение, чего мне не хватает?

ViewController:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    lazy var tableView: UITableView = {
        let table = UITableView()
        table.backgroundColor = .white
        table.translatesAutoresizingMaskIntoConstraints = false
        table.register(TableViewCell.self, forCellReuseIdentifier: "cellId")
        table.dataSource = self
        table.delegate = self
        return table
    }()


    let arr:[Int:UIColor] = [345: UIColor.random, 422: .random, 23: .random, 344: .random,200: .random,140: .random]

    var pickerDataVisitLocation = [203: "Home", 204: "Hospital", 205: "Other"]

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .red

        self.view.addSubview(tableView)
//
        tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
        tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
        tableView.tableFooterView = UIView()
    }
}

extension ViewController {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! TableViewCell
        let value:UIColor = Array(arr)[indexPath.row].value
        let key = Array(arr)[indexPath.row].key

        cell.setupViews(he: CGFloat(key), color: value)
        return cell
    }

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

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

extension UIColor {
    static var random: UIColor {
        return UIColor(red: .random(in: 0...1),
                       green: .random(in: 0...1),
                       blue: .random(in: 0...1),
                       alpha: 1.0)
    }
}

TableViewCell:

    import UIKit

    class TableViewCell: UITableViewCell {


        override func awakeFromNib() {
            super.awakeFromNib()


        }

        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)


        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }

        func setupViews(he:CGFloat, color:UIColor) {

            let v:UIView = UIView()
            v.translatesAutoresizingMaskIntoConstraints = false
            self.addSubview(v)

            v.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
            v.backgroundColor = color
            v.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
            v.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
            v.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
            v.heightAnchor.constraint(equalToConstant: he).isActive = true
            #warning("here is constraint error conflict with bottomAnchor and heightAnchor, need correct solution")
        }

    }

Ответы [ 2 ]

2 голосов
/ 15 марта 2019

В вашей ситуации высота доступна в источнике данных arr, поэтому вам не нужно:

  1. Ограничение по высоте
  2. estimatedHeightForRowAtIndexPath

Все, что вам нужно, это вернуть фактическую высоту в heightForRowAtIndexPath, но сначала ваш источник данных arr:[Int:UIColor] - это Dictionary, и я не буду полагаться на его порядок, давайте изменим его на Array из Tuples:

var dataSource: [(height: CGFloat, color: UIColor)] = [
    (345, .random),
    (422, .random),
    (23, .random),
    (344, .random),
    (200, .random),
    (140, .random)
]

Теперь используйте следующие методы UITableView Delegate / DataSource:

extension ViewController: UITableViewDataSource, UITableViewDelegate {

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

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return dataSource[indexPath.row].height
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! TableViewCell
        cell.setupViews(color: dataSource[indexPath.row].color)
        return cell
    }

}

Поскольку вам не нужно ограничение по высоте, я удалил параметр he из setupViews method

1 голос
/ 15 марта 2019

Вы делаете пару вещей неправильно ...

Сначала ячейки используются повторно (следовательно, dequeueReusableCell), но ваш setupViews() функция добавляет новое подпредставление каждый раз, когда ячейка используется повторно .

Это означает, что при прокрутке и повторном использовании ячеек вы получите 2, 3, 4 ... дюжину подпредставлений, все с конфликтующими ограничениями.

Переместите addSubview()к общей функции инициализации в вашей ячейке, поэтому представление создается и добавляется только один раз.

Здесь также необходимо настроить ограничения.

Чтобы изменить высоту подпредставления в качестве приложения.разработан, вы хотите изменить .constant ограничения высоты подпредставления.

Вот ваш измененный код.Я добавил достаточно комментариев в коде, чтобы это было понятно:

class HattoriTableViewCell: UITableViewCell {

    // the view to add as a subview
    let myView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    // the constraint we'll use for myView's height
    var myViewHeightConstraint: NSLayoutConstraint!

    override func awakeFromNib() {
        super.awakeFromNib()
        commonInit()
    }

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {

        // add the subview
        self.addSubview(myView)

        // constrain it to all 4 sides
        myView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
        myView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        myView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        myView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true

        // create the height constraint
        myViewHeightConstraint = myView.heightAnchor.constraint(equalToConstant: 1)

        // needs Priority less-than 1000 (default) to avoid breaking constraints
        myViewHeightConstraint.priority = UILayoutPriority.init(999)

        // activate it
        myViewHeightConstraint.isActive = true

    }

    func setupViews(he:CGFloat, color:UIColor) {

        // set myView's background color
        myView.backgroundColor = color

        // change myView's height constraint constant
        myViewHeightConstraint.constant = he

    }

}

class HattoriViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    lazy var tableView: UITableView = {
        let table = UITableView()
        table.backgroundColor = .white
        table.translatesAutoresizingMaskIntoConstraints = false
        table.register(HattoriTableViewCell.self, forCellReuseIdentifier: "cellId")
        table.dataSource = self
        table.delegate = self
        return table
    }()


    let arr:[Int:UIColor] = [345: UIColor.random, 422: .random, 23: .random, 344: .random,200: .random,140: .random]

    var pickerDataVisitLocation = [203: "Home", 204: "Hospital", 205: "Other"]

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .red

        self.view.addSubview(tableView)
        //
        tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
        tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
        tableView.tableFooterView = UIView()

        // use a reasonable value -- such as the average of what you expect (if known)
        tableView.estimatedRowHeight = 200
    }
}

extension HattoriViewController {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! HattoriTableViewCell

        let value:UIColor = Array(arr)[indexPath.row].value
        let key = Array(arr)[indexPath.row].key

        cell.setupViews(he: CGFloat(key), color: value)

        return cell
    }

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

}

extension UIColor {
    static var random: UIColor {
        return UIColor(red: .random(in: 0...1),
                       green: .random(in: 0...1),
                       blue: .random(in: 0...1),
                       alpha: 1.0)
    }
}
...