UIStackView не обновляется после обновления высоты подпредставления (UITableView без прокрутки), находясь внутри ScrollView

Как заставить UIStackView перераспределять его суб-UITableViews, пока stackView находится внутри прокрутки?

Моя иерархия макетов основана на официальной документации Apple о Динамический контент для StackViews

 - UISCrollView
   - UIStackView
     - UIView A
     - UIView B
     - UIView C
     - UITableView X
     - UITableView Y
     - UIView D

Ограничения установлены в соответствии с документацией. Исходный макет StackView правильный, показывая все видимые подпредставления. При принудительном расширении обычных представлений за пределы высоты экрана прокрутка работает должным образом. Также при просмотре макета в раскадровке все складывается, как и ожидалось.
На данный момент UITableView пусты. Как только я добавляю содержимое в tableView, возникает проблема.

Проблема
Когда я динамически обновляю TableView, вызывая .reloadData() для обоих из них, я вижу, как появляется их содержимое. (благодаря этому ответу о tableViews без прокрутки), но UIStackView не складывает UITableViews.

  • UIView D расположен ниже UIView C
  • UITableView X и UITableView Y также расположены ниже UIView B

Я предполагаю, что мне нужно аннулировать представление стека или каким-то образом заставить его перераспределять его подпредставления. Как я могу это сделать?

введите здесь описание изображения


person Tom Bevelander    schedule 04.09.2016    source источник


Ответы (1)


Во-первых, предупреждение:

То, чего вы пытаетесь достичь, на самом деле не является стандартным поведением iOS. Сначала вам следует рассмотреть другой подход, например создание единого сгруппированного табличного представления с несколькими разделами. Вы можете реализовать настраиваемые представления внутри вашего табличного представления в виде верхних или нижних колонтитулов разделов.


Теперь, если вы действительно хотите использовать свой оригинальный подход...

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

Вы можете добиться этого, создав подкласс UITableView и переопределив его intrinsicContentSize(), как предлагает Роб в этом ответе на аналогичный вопрос.

Или вы добавляете ограничение по высоте к каждому из ваших табличных представлений и динамически устанавливаете их константы в коде. Краткий пример:

  1. Добавьте оба вида таблицы в представление вертикального стека в Interface Builder.
  2. Дайте обоим табличным представлениям начальное и конечное ограничение, чтобы прикрепить их левый и правый края к представлению стека.
  3. Создайте выходы для ваших табличных представлений в соответствующем контроллере представления:

    @IBOutlet weak var tableView1: UITableView!
    @IBOutlet weak var tableView2: UITableView!
    
    @IBOutlet weak var tableView1HeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var tableView2HeightConstraint: NSLayoutConstraint! 
    
  4. Переопределите метод updateViewConstraints() этого контроллера представления:

    override func updateViewConstraints() {
        super.updateViewConstraints()
        tableView1HeightConstraint.constant = tableView1.contentSize.height
        tableView2HeightConstraint.constant = tableView2.contentSize.height
    }
    
  5. Теперь всякий раз, когда содержимое любого из ваших табличных представлений изменяется (например, когда вы добавляете или удаляете строки или изменяете содержимое ячейки), вам нужно сообщить вашему контроллеру представления, что ему необходимо обновить свои ограничения. Допустим, у вас есть кнопка, которая добавляет ячейку в tableView1 каждый раз, когда вы ее нажимаете. Вы можете реализовать его действие следующим образом:

    @IBAction func buttonTappen(sender: AnyObject) {
        // custom method you implement somewhere else in your view controller
        addRowToTableView1DataSource()
        // reload table view with the updated data source
        tableView1.reloadData()
        // triggers an updateViewConstraints() call
        view.setNeedsUpdateConstraints()
    }
    

tl;dr:

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

person Mischa    schedule 04.09.2016
comment
Спасибо @Миша. Исходя из Android, это было хотя бы одно! Я начал с одного представления таблицы, как вы предлагаете. Это стало настолько сложным, что я решил попробовать установку stackview. В моем случае таблицы имеют ограниченное максимальное количество строк (4 и 14), поэтому производительность должна быть хорошей. Огромнейшее спасибо за развернутый ответ! - person Tom Bevelander; 05.09.2016
comment
Я понимаю, что вы имеете в виду: вы получаете гигантский метод tableView(_:cellForRowAt:) с множеством переключателей или операторов if для разных indexPaths. В этом случае ваш подход может быть даже более аккуратным решением. Тем не менее, ваша установка по-прежнему выглядит управляемой для меня с одним представлением таблицы: вы можете поместить A, B и C в представление стека и установить это представление стека в качестве заголовка первого раздела. Верните D в качестве нижнего колонтитула второго раздела. Теперь все, что вам нужно сделать внутри tableView(_:cellForRowAt:), — это различить раздел 1 и раздел 2, которые соответствуют представлениям таблиц 1 и 2 в вашей текущей настройке. - person Mischa; 05.09.2016