Как работает 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];