Как работает Scrollview с автозагрузкой и почему установка нижнего вертикального ограничения пространства заставляет работать?

Я пытаюсь понять, как работает UIScrollView в среде автоматического макета. До сих пор я пробовал читать документацию Apple, исследование, исследования Google и изучение рабочего примера Мэтта Нойберга.

В документации Apple 6.1 говорится:

Центральное понятие объекта UIScrollView (или, просто, прокрутки view) заключается в том, что это представление, происхождение которого регулируется по содержимому Посмотреть. Он закрепил контент до его кадра, который обычно (но не обязательно) совпадает с таковым в главном окне приложения. просмотр прокрутки отслеживает движения пальцев и регулирует начало координат соответственно. Представление, показывающее его содержимое через "свиток" представление рисует эту часть себя на основе нового происхождения, привязано к смещению в представлении содержимого. Сам вид прокрутки нет чертежа, за исключением отображения вертикальной и горизонтальной прокрутки показатели. В представлении прокрутки должен быть указан размер представления содержимого, поэтому он знает, когда прекратить прокрутку; по умолчанию он "отскакивает" назад, когда прокрутка превышает границы содержимого.

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

Чтобы было проще обсуждать, скажем, у нас есть 3 представления в общем случае - представление контроллера основного вида по умолчанию (A), его подвью - это UIScrollview (B), а UIScrollview имеет одно подзаголовок, UIView (C). Скажем, мы хотим, чтобы (C) составлял 1000 единиц.

Итак, мы заходим в конструктор интерфейса, выбираем контроллер представления в панели рассказов, а на вкладке инспектора атрибутов изменяем размер на произвольную форму. Для видов (A), (B) и (C) мы меняем высоту на 1000 на вкладке инспектора размера.

Ограничения между (A) и основным окном

Время установки ограничений. В документации четко указано: "(Scrollview) зажимает содержимое в свой кадр, который обычно (...) совпадает с содержимым главного окна приложений". В нашем примере (A) будет совпадать с основным окном приложения, и для этого не требуются ограничения.

Ограничения между (A) и (B)

Теперь в документации было ясно, что (B) точно совпадает с (A), поэтому мы будем устанавливать между ними 4 ограничения, ведущее пространство, конечное пространство, верхнее пространство и нижнее пространство, чтобы контролировать все с константой 0.

Ограничения между (B) и (C)

Документация здесь не так прямолинейна. В нем говорится, что (B) происхождение регулируется по (C), поэтому (B) обязательно должно быть меньше (C). Поскольку мы знаем, что прокрутка будет только вверх и вниз, мы можем ограничить левый и правый края между (B) и (C) до нуля, и мы всегда хотим, чтобы это было посередине, поэтому мы добавим центр x выравнивание. Мы добавим к ним 3 ограничения, ведущее пространство и конечное пространство для просмотра с константой 0 и выравниванием по центру x. Чтобы позиционировать представление, нам нужно что-то для верхнего и нижнего уровня, и, честно говоря, я не уверен, как эти ограничения должны быть настроены на основе документации. На основе имитации Matt Neuberg example Я сделал их верхним пространством для наблюдения с константой нуля и нижним пространством для наблюдения с любой константой, которую он генерирует по умолчанию. Это нижнее пространство для ограничения супервизора является особенным (по-видимому), отныне оно упоминается как "specialConstraint".

Итак... что?! Мы начали этот параграф, сказав, что (B) определенно будет меньше, чем (C), и закончил его, установив ограничения, чтобы сделать их точно того же размера. Вопрос 1 - Почему это?

Ограничения на (C) самим собой

Мы знаем, что (C) должно быть больше, чем (B), так что (B) имеет что-то, что можно прокрутить, и (C) должно определить его размер на основе собственных ограничений. Достаточно легко установить 1 ограничение, высота = 1000.

specialConstraint

Теперь мы создаем выход для specialConstraint в контроллере представления, а в методе viewDidLoad мы устанавливаем self.specialConstraint.constant = 0; Это означает, что нижняя часть содержимого должна быть привязана точно к нижней части прокрутки, что не даст вам ничего прокрутить вниз. Но это работает в примере Мэтта Нойберга. Вопрос 2 - почему это?

Что действительно происходит при прокрутке Scrollview

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

Однако, основываясь на моем чтении, кадр scrollview фактически выполняет перемещение, например, увеличительное стекло на фиксированной странице.

Вопрос 3. Может ли кто-нибудь объяснить, что происходит, когда свиток scrollview, какой из исходных фреймов объекта меняется, и почему это делается так?

Вопрос 4 - Может ли кто-нибудь объяснить, как ограничения должны быть установлены между (A), (B) и (C) на простом английском языке?

Ответ 1

Рабочий пример

Привет. Сначала, вот рабочий пример прокрутки, который вызывается пару дней назад.

[self addSubview:scrollView];

id views = NSDictionaryOfVariableBindings(scrollView);
[self addConstraints:PVVFL(@"H:|[scrollView]|").withViews(views).asArray];
[self addConstraints:PVVFL(@"V:|[scrollView]|").withViews(views).asArray];

Ограничения, введенные в код и с помощью Parus lib.

Как вы можете заметить, просмотр прокрутки привязан к этому супервизу. Другими словами, он полностью заполняет его.

Пара строк ранее я настраиваю дерево scrollView.

id views = NSDictionaryOfVariableBindings(photoView, storeNameLabel, selectStoreButton, tagsLabel, tagsContainer, editTagsButton, commentView, sendButton, cancelButton);
[scrollView addConstraints:PVVFL(@"V:|-15-[photoView(150)]").withViews(views).asArray];
[scrollView addConstraints:PVVFL(@"|-15-[photoView]-15-|").withViews(views).asArray];
[scrollView addConstraint:PVCenterXOf(photoView).equalTo.centerXOf(scrollView).asConstraint];

[scrollView addConstraint:PVTopOf(storeNameLabel).equalTo.bottomOf(photoView).plus(20).asConstraint];
[scrollView addConstraints:
 PVVFL(@"|-15-[storeNameLabel]-(>=15)-[selectStoreButton]-15-|")
 .alignAllBaseline.withViews(views).asArray];
[selectStoreButton setContentCompressionResistancePriority:UILayoutPriorityRequired
                                                   forAxis:UILayoutConstraintAxisHorizontal];

[scrollView addConstraints:
 PVVFL(@"V:[storeNameLabel]-15-[tagsLabel][tagsContainer]").alignAllLeft.withViews(views).asArray];
[scrollView addConstraint:PVRightOf(tagsContainer).equalTo.rightOf(selectStoreButton).asConstraint];
[scrollView addConstraint:PVTopOf(editTagsButton).equalTo.bottomOf(tagsContainer).plus(10).asConstraint];
[scrollView addConstraint:PVWidthOf(editTagsButton).equalTo.widthOf(tagsContainer).asConstraint];
[scrollView addConstraint:PVLeftOf(editTagsButton).equalTo.leftOf(tagsContainer).asConstraint];

[scrollView addConstraint:PVTopOf(commentView).equalTo.bottomOf(editTagsButton).plus(10).asConstraint];
[scrollView addConstraint:PVWidthOf(commentView).equalTo.widthOf(editTagsButton).asConstraint];
[scrollView addConstraint:PVLeftOf(commentView).equalTo.leftOf(editTagsButton).asConstraint];
[scrollView addConstraint:PVHeightOf(commentView).moreThan.constant(100).asConstraint];
[scrollView addConstraint:PVLeftOf(cancelButton).equalTo.leftOf(commentView).asConstraint];
[scrollView addConstraints:PVVFL(@"[cancelButton]-(>=15)-[sendButton(==cancelButton)]").alignAllBaseline.withViews(views).asArray];
[scrollView addConstraint:PVRightOf(sendButton).equalTo.rightOf(commentView).asConstraint];
[scrollView addConstraints:PVVFL(@"V:[commentView]-10-[cancelButton]-10-|").withViews(views).asArray];

Как вы видите, это довольно сложный макет. Но ключевые моменты этого: внутренние представления фиксировали высоты, обеспечиваемые их внутренними размерами контента. Объединив все вместе, они попытались увеличить высоту scrollView. Но высота scrollView фиксируется self.view!!! Решатель Constraint должен генерировать ошибку. Но,

Теория

Apple имеет специальное Техническое примечание для этого случая. И есть интересный момент:

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

Также в этой заметке приводятся некоторые примеры различных подходов при работе со списками прокрутки и автомакетами.

Теперь я постараюсь ответить на ваши вопросы.

Ответы

Вопрос 1 - Почему это?

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

Вопрос 2 - почему это?

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

Вопрос 3. Может ли кто-нибудь объяснить, что происходит, когда свиток scrollview, какой из исходных фреймов объекта меняется, и почему это делается так?

Великое объяснение этого процесса вы можете найти в этой статье: Понятие прокрутки > , но короткий ответ: просмотр UIScroll выполняет прокрутку, изменяя self.bounds.origin точка.

Вопрос 4 - Может ли кто-нибудь объяснить, как ограничения должны быть установлены между (A), (B) и (C) на простом английском языке?

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

Если ответ на этот вопрос еще не ясен, вы можете прочитать этот ответ с самого начала.

Ответ 2

Я нашел много примеров, как это сделать, но не один, объясняющий, как установить contendize в Runtime. Итак, с помощью кнопки.

На самом деле все сводится к тому, чтобы иметь отношение между вашим содержимым scrollviews и scrollView.

Теперь вы можете прокручивать прокрутку и изменять размер без кода только в раскадровке. Только вы должны сделать это правильно.

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

Это трудно понять. Я поместил код в github http://goo.gl/qrXANp, и вы можете найти видео об этом здесь... https://www.youtube.com/watch?v=4oCWxHLBQ-A

Ответ 3

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

UIView → UIScrollView → ContentView

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

   self.ContentView = [[UIView alloc] initWithFrame:CGRectZero];
    self.ContentView.translatesAutoresizingMaskIntoConstraints = NO;
    self.scrollview = [[UIScrollView alloc] initWithFrame:CGRectZero];
    self.scrollview.translatesAutoresizingMaskIntoConstraints = NO;
    [self.scrollview sizeToFit];

    [self.scrollview addSubview:self.ContentView];
    [self.view addSubview:self.scrollview];

    NSMutableDictionary *views = [[NSMutableDictionary alloc] init];

    [views setValue:self.ContentView forKey:@"ContentView"];
    [views setValue:self.scrollview forKey:@"scrollview"];
    [views setValue:self.view forKey:@"mainView"];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[scrollview]-0-|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[scrollview]-0-|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];

    [self.scrollview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[ContentView]|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];

    [self.scrollview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[ContentView]|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[ContentView(==mainView)]"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:views]];

    self.view.contentMode = UIViewContentModeRedraw;

    [self.view setNeedsUpdateConstraints];