UIScrollView, который расширяется с содержанием "instrinsic size" до высоты X, а затем прокручивает

Я пытаюсь воспроизвести поведение раздела заголовка и сообщения в предупреждении.

enter image description here

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

Что я прочитал:

Статьи

Qaru

Ответ может быть там, но я не смог его абстрагировать.

Что я пробовал:

Сосредоточившись только на представлении прокрутки с двумя метками, я попытался сделать минимальный пример, в котором размер родительского представления будет изменяться в соответствии с внутренней высотой представления прокрутки. Я играл с большим количеством ограничений. Вот одна комбинация (среди многих), которая не работает:

enter image description here

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

Ответ 1

Я думаю, что достиг эффекта, похожего на тот, который вы хотели с чистым автоматическим макетом.

СТРУКТУРА

Сначала позвольте мне показать вам мою структуру:

enter image description here

Представление содержимого - это представление с белым фоном, а Caller View и Bottom View имеют фиксированную высоту. В нижней части есть ваша кнопка, а в Caller View есть название.

РЕШЕНИЕ

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

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

enter image description here

Однако это создает две проблемы: представление прокрутки по-прежнему не знает, какая высота должна быть, и теперь вы можете изменить размер, и представление прокрутки пропустит размер его содержимого (если содержимое меньше максимального размера).

Итак, чтобы решить обе проблемы, я добавил одинаковую высоту с меньшим приоритетом из представления в представлении прокрутки и в представлении прокрутки

enter image description here

Я надеюсь, что это может помочь вам.

Ответ 2

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

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

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

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

Ответ 3

Вы должны иметь возможность достичь этого решения с помощью чистого автозапуска.

Обычно, если я хочу, чтобы метки возрастали по мере роста их содержимого по вертикали, я делаю это

[label setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; 
[label setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

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

Ответ 4

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

Во-первых, высота заголовка и нижнего действия фиксированы Контент имеет переменную высоту. Вы можете добавить его в mainView как один дочерний элемент, используя размер шрифта, затем вызвать layoutIfNeeded, затем его высоту можно рассчитать и сохранить как XX. Затем удалил его из mainView.

Во-вторых, используя обычное ограничение для размещения части содержимого с scrollView, mainView имеет ограничение высоты XX и setContentCompressionResistancePriority (.defaultLow, для:.vertical).

Наконец, предупреждение может показывать точный размер при коротком контенте и ограниченный размер при длинном прокрутке.

Ответ 5

Мне удалось добиться такого точного поведения только с ограничениями AutoLayout. Вот общая демонстрация того, как это сделать: она может быть применена к вашей иерархии представлений, как вы считаете нужным.

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let kTestContentHeight: CGFloat = 1200

        // Subview that will shrink to fit content and expand up to 50% of the view controller height
        let modalView = UIView()
        modalView.backgroundColor = UIColor.gray

        // Scroll view that will facilitate scrolling if the content > 50% of view controller height
        let scrollView = UIScrollView()
        scrollView.backgroundColor = .yellow

        // Content which has an intrinsic height
        let contentView = UIView()
        contentView.backgroundColor = .green


        // add modal view
        view.addSubview(modalView)
        modalView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([modalView.leftAnchor.constraint(equalTo: view.leftAnchor),
                                     modalView.rightAnchor.constraint(equalTo: view.rightAnchor),
                                     modalView.heightAnchor.constraint(lessThanOrEqualTo: view.heightAnchor,
                                                                       multiplier: 0.5),
                                     modalView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])

        let expandHeight = modalView.heightAnchor.constraint(equalTo: view.heightAnchor)
        expandHeight.priority = UILayoutPriority.defaultLow
        expandHeight.isActive = true

        // add scrollview to modal view
        modalView.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([scrollView.topAnchor.constraint(equalTo: modalView.topAnchor),
                                     scrollView.leftAnchor.constraint(equalTo: modalView.leftAnchor),
                                     scrollView.rightAnchor.constraint(equalTo: modalView.rightAnchor),
                                     scrollView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)])


        // add content to scrollview
        scrollView.addSubview(contentView)
        contentView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
                                     contentView.widthAnchor.constraint(equalTo: modalView.widthAnchor),
                                     contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
                                     contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
                                     contentView.heightAnchor.constraint(equalToConstant: kTestContentHeight)])
        let contentBottom = contentView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)
        contentBottom.priority = .defaultLow
        contentBottom.isActive = true
    }

}