Динамически размер ячейки таблицы просмотра с использованием ограничений автоматического макета

Update

Я полностью пересмотрел этот вопрос после моих последних результатов.

Цель

Моя цель - реализовать следующий эффект:

  • Существует простой вид таблицы
  • Пользователь выбирает строку
  • Выбранная строка расширяется, показывая другую метку ниже оригинальной.

Обратите внимание, что я знаю, что это может быть достигнуто путем вставки/удаления ячеек ниже выбранного, У меня уже есть успешная реализация с использованием этого метода.

На этот раз я хочу попытаться добиться этого, используя ограничения Auto Layout.

Текущее состояние

У меня есть образец проекта, доступный для любого, кто может проверить, а также открылась проблема. Подводя итог, вот что я пробовал до сих пор:

У меня здесь есть следующие взгляды:

  • Вид содержимого ячейки
  • Вид сверху, содержащий основную метку ( "основной вид" )
  • Вид снизу под основным видом, содержащий первоначально скрытую метку ( "подробный просмотр" )

Я установил ограничения автоматической компоновки в своей ячейке следующим образом (обратите внимание, что это строго псевдоязык):

  • mainView.top = contentView.top
  • mainView.leading = contentView.leading
  • mainView.trailing = contentView.trailing
  • mainView.bottom = detailView.top
  • detailView.leading = contentView.leading
  • detailView.trailing = contentView.trailing
  • detailView.bottom = contentView.bottom
  • detailView.height = 0

У меня есть собственный подкласс UITableViewCell с несколькими выходами, но наиболее важным здесь является выход для ограничения высоты, упомянутого ранее: идея здесь заключается в том, чтобы установить его constant в 0 по умолчанию, но когда ячейка, установите его на 44, чтобы он стал видимым:

override func setSelected(selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    detailViewHeightConstraint.constant = selected ? detailViewDefaultHeight : 0
    UIView.animateWithDuration(0.3) {
        self.layoutIfNeeded()
    }
}

У меня есть следующий результат:

Current status

Итак, эффект работает, но не совсем так, как я изначально представлял. Вместо того, чтобы нажимать основной вид вверх, я хочу, чтобы высота ячейки расти, когда отображается подробный вид, а сокращается, когда она скрыта.

Я рассмотрел иерархию макета во время выполнения:

  • Исходное состояние в порядке. Высота представления содержимого равна высоте моего основного вида (в этом случае это 125 точек).

Screenshot initial

  • Когда ячейка выбрана, ограничение высоты подробного представления увеличивается до 44 точек, а два представления правильно располагаются вертикально. Но вместо расширения содержимого ячейки, но вместо этого основное представление сжимается.

Screenshot detailed

Вопрос

Мне нужно следующее: высота представления содержимого ячейки таблицы должна быть равна

  • высота основного вида, когда ограничение высоты подробного представления равно 0 (в настоящее время это работает)
  • высота главного представления + подробная высота представления, когда ограничение высоты просмотра подробного представления установлено правильно (это не работает).

Как мне установить ограничения для этого?

Ответы

Ответ 1

После значительного объема исследований, я думаю, что нашел решение с помощью этой замечательной статьи.

Ниже приведены шаги, необходимые для изменения размера ячейки:

В основном и подробном представлениях я изначально устанавливал метки в горизонтальном и вертикальном направлении. Этого недостаточно для самостоятельной калибровки клеток. Первое, что мне нужно - это настроить мой макет с использованием ограничений вертикального расстояния вместо простого выравнивания:

Main View

Кроме того, вы должны установить сопротивление вертикального сжатия основного контейнера 1000.

Детальный вид немного сложнее: помимо создания соответствующих вертикальных ограничений вам также необходимо играть со своими приоритетами, чтобы достичь желаемого эффекта:

  • Высота контейнера Detail ограничена 44 баллами, но для того, чтобы сделать его необязательным, установите его приоритет 999 (согласно документам, все, что ниже "Требуется", будет считаться таковым).
  • Внутри контейнера Detail установите ограничения по вертикальному расстоянию и дайте им приоритет 998.

Detail View

Основная идея заключается в следующем:

  • По умолчанию ячейка сбрасывается. Чтобы достичь этого, мы должны программно установить константу ограничения длины контейнера Detail равным 0. Поскольку его приоритет выше, чем вертикальные ограничения в представлении содержимого ячейки, последний будет игнорироваться, поэтому контейнер Detail будет скрыт.
  • Когда мы выбираем ячейку, мы хотим ее расширить. Это означает, что вертикальные ограничения должны взять под контроль: мы устанавливаем ограничение на ограничение приоритета Detail Container на что-то низкое (я использовал 250), поэтому он будет проигнорирован в пользу ограничений в представлении контента.

Мне пришлось изменить подкласс UITableViewCell для поддержки этих операций:

// `showDetails` is exposed to control, whether the cell should be expanded
var showsDetails = false {
    didSet {
        detailViewHeightConstraint.priority = showsDetails ? lowLayoutPriority : highLayoutPriority
    }
}

override func awakeFromNib() {
    super.awakeFromNib()
    detailViewHeightConstraint.constant = 0
}

Чтобы вызвать поведение, мы должны переопределить tableView(_:didSelectRowAtIndexPath:):

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRowAtIndexPath(indexPath, animated: false)

    switch expandedIndexPath {
    case .Some(_) where expandedIndexPath == indexPath:
        expandedIndexPath = nil
    case .Some(let expandedIndex) where expandedIndex != indexPath:
        expandedIndexPath = nil
        self.tableView(tableView, didSelectRowAtIndexPath: indexPath)
    default:
        expandedIndexPath = indexPath
    }
}

Обратите внимание, что я ввел expandedIndexPath для отслеживания нашего расширенного индекса:

var expandedIndexPath: NSIndexPath? {
    didSet {
        switch expandedIndexPath {
        case .Some(let index):
            tableView.reloadRowsAtIndexPaths([index], withRowAnimation: UITableViewRowAnimation.Automatic)
        case .None:
            tableView.reloadRowsAtIndexPaths([oldValue!], withRowAnimation: UITableViewRowAnimation.Automatic)
        }
    }
}

Настройка свойства приведет к тому, что представление таблицы перезагрузит соответствующие индексы, предоставив нам прекрасную возможность рассказать ячейке, если она должна расширяться:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ExpandableTableViewCell

    cell.mainTitle = viewModel.mainTitleForRow(indexPath.row)
    cell.detailTitle = viewModel.detailTitleForRow(indexPath.row)

    switch expandedIndexPath {
    case .Some(let expandedIndexPath) where expandedIndexPath == indexPath:
        cell.showsDetails = true
    default:
        cell.showsDetails = false
    }

    return cell
}

Последний шаг состоит в том, чтобы включить самоограничение в viewDidLoad():

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.contentInset.top = statusbarHeight
    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 125
}

Вот результат:

Result

Теперь ячейки корректно сортируют. Вы можете заметить, что анимация все еще немного странная, но исправление, которое не входит в сферу этого вопроса.

Заключение: это было намного сложнее, чем должно быть. 😀 Я действительно надеюсь увидеть некоторые улучшения в будущем.

Ответ 2

Это в obj-c, но я уверен, что вы справитесь с этим:

Добавьте в свой viewDidLoad:

self.tableView.estimatedRowHeight = self.tableView.rowHeight; self.tableView.rowHeight = UITableViewAutomaticDimension;

Это позволит самонастраивать ячейки для вашего tableView и работать на iOS8 +