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

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

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

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

статьи

Переполнение стека

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

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

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

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

3 ответа

Решение

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

СТРУКТУРА

Во-первых, позвольте мне показать вам мою структуру:

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

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

РЕШЕНИЕ

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

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

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

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

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

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

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

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

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

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

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

It can be done in Interface builder using auto layout without any difficulties.

  1. set outer container view ("Parent container for scrollview" in your sample) height constraint with "less than or equal" relation.

2.add "equal heights" constraint to content view and scroll view. Then set low priority for this constraint.

That's all. Now your scrollview will be resized by height if content height changed, but only to max height limited by outer view height constraint.

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

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

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

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

Вы можете сделать это довольно просто с двумя ограничениями

  • Ограничение 1: ScrollView.height <= MAX_SIZE. Priority = обязательно
  • Ограничение 2: ScrollView.height = ScrollView.contentSize.height. Priority = DefaultHigh

AutoLayout будет "пытаться" сохранить scrollView равным contentSize, но "сдастся", когда он будет соответствовать максимальной высоте, и остановится на этом.

единственная сложная часть - установить высоту для ограничения 2.

Когда мой UIStackView находится в UIViewController, я делаю это в viewWillLayoutSubviews

Если для этого вы создаете подкласс UIScrollView, вы можете сделать это в updateConstraints

что-то типа

override func viewWillLayoutSubviews() {

    scrollViewHeightConstraint?.constant = scrollView.contentSize.height     

    super.viewWillLayoutSubviews()
}

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

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

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

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

Мне удалось добиться этого точного поведения только с ограничениями 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's height
        let modalView = UIView()
        modalView.backgroundColor = UIColor.gray

        // Scroll view that will facilitate scrolling if the content > 50% of view controller's 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
    }

}
Другие вопросы по тегам