Допустим, я добавил больше видов в UIStackView, которые можно отобразить, как я могу сделать UIStackViewпрокрутку?
Допустим, я добавил больше видов в UIStackView, которые можно отобразить, как я могу сделать UIStackViewпрокрутку?
Ответы:
В случае, если кто-то ищет решение без кода , я создал пример, чтобы сделать это полностью в раскадровке, используя Auto Layout.
Вы можете получить его из GitHub .
В основном, чтобы воссоздать пример (для вертикальной прокрутки):
UIScrollViewи установите его ограничения.UIStackViewкUIScrollViewLeading, Trailing, Topи Bottomдолжны быть равны те изUIScrollViewWidthограничения между UIStackViewи UIScrollView.UIStackViewUIViewsвUIStackViewЗамените Widthна Heightшаге 4 и установите Axis= Horizontalна шаге 5, чтобы получить горизонтальный UIStackView.
Apple Auto Layout Guide содержит целый раздел по работе с представлениями прокрутки . Некоторые соответствующие фрагменты:
- Прикрепите верхний, нижний, передний и задний края представления содержимого к соответствующим краям представления прокрутки. Представление содержимого теперь определяет область содержимого представления прокрутки.
- (Необязательно) Чтобы отключить горизонтальную прокрутку, установите ширину представления содержимого, равную ширине представления прокрутки. Представление содержимого теперь заполняет представление прокрутки по горизонтали.
- (Необязательно) Чтобы отключить вертикальную прокрутку, установите высоту представления содержимого, равную высоте представления прокрутки. Представление содержимого теперь заполняет представление прокрутки по горизонтали.
Более того:
Ваш макет должен полностью определять размер представления контента (кроме случаев, определенных в шагах 5 и 6). … Когда представление содержимого выше, чем представление прокрутки, представление прокрутки включает вертикальную прокрутку. Когда представление содержимого шире, чем представление прокрутки, представление прокрутки включает горизонтальную прокрутку.
Подводя итог, представление содержимого представления прокрутки (в данном случае, представление стека) должно быть прикреплено к его краям, и его ширина и / или высота должны быть ограничены иным образом. Это означает, что содержимое представления стека должно быть ограничено (прямо или косвенно) в направлении (ях), в котором желательна прокрутка, что может означать, например, добавление ограничения высоты для каждого представления в представлении стека с вертикальной прокруткой. Ниже приведен пример того, как разрешить вертикальную прокрутку представления прокрутки, содержащего представление стека:
// Pin the edges of the stack view to the edges of the scroll view that contains it
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
// Set the width of the stack view to the width of the scroll view for vertical scrolling
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
Need constraints for: Y position or height.эта ошибка исчезает, только если вы устанавливаете ограничение ширины и высоты для стекового представления, которое отключает вертикальную прокрутку. Этот код все еще работает для вас?
Ограничения в ответе с наибольшим количеством голосов здесь работали для меня, и я вставил изображение ограничений ниже, как создано в моей раскадровке.
Я столкнулся с двумя проблемами, о которых другие должны знать:
После добавления ограничений, аналогичных тем, которые содержатся в принятом ответе, я получу красную ошибку автоматического определения местоположения Need constraints for: X position or width. Это было решено путем добавления UILabel в качестве подпредставления стекового представления.
Я добавляю подпредставления программно, поэтому у меня изначально не было подпредставлений на раскадровке. Чтобы избавиться от ошибок автоматического размещения, добавьте подпредставление в раскадровку, а затем удалите его при загрузке перед добавлением ваших реальных подпредставлений и ограничений.
Первоначально я пытался добавить UIButtonsв UIStackView. Кнопки и представления будут загружаться, но представление прокрутки не будет прокручиваться . Это было решено путем добавленияUILabels в Stack View вместо кнопок. Используя те же ограничения, эта иерархия представления с UILabels прокручивается, а UIButton - нет.
Я смущен этой проблемой, поскольку UIB-кнопки , кажется, имеют IntrinsicContentSize (используется стековым представлением). Если кто-нибудь знает, почему кнопки не работают, я хотел бы знать, почему.
Вот моя иерархия представления и ограничения, для справки:
![ограничения для просмотра стека в представлении прокрутки [1]](https://i.stack.imgur.com/uomXU.png)
Как говорит Эйк, UIStackViewи UIScrollViewиграйте вместе, смотрите здесь .
Ключевым моментом является то, что он UIStackViewобрабатывает переменную высоту / ширину для различного содержимого, а UIScrollViewзатем хорошо выполняет свою работу по прокрутке / отскоку этого содержимого:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)
}
Я хотел сделать то же самое и наткнулся на этот отличный пост. Если вы хотите сделать это программно с помощью API привязки, это путь.
Подводя итог, вставьте свой UIStackViewв ваш UIScrollView, и установите ограничения привязки для, UIStackViewчтобы соответствовать тем из UIScrollView:
stackView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
stackView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
stackView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
Пустая полноэкранная сцена
Добавить вид прокрутки. Удерживая клавишу Control, перетащите из вида прокрутки в базовый вид , добавьте левый-правый-верхний-нижний, все ноль.
Добавьте представление стека в представлении прокрутки. Удерживая клавишу Control, перетащите из представления стека в представление прокрутки , добавьте левый-правый-верхний-нижний, все ноль.
Поместите две или три метки внутри стека.
Для наглядности сделайте цвет фона метки красным. Установите высоту этикетки до 100.
Теперь установите ширину каждой UILabel:
Удивительно, но перетаскиванием элемента управления из в UILabelпредставление прокрутки, а не в представление стека, и выберите равную ширину .
Повторить:
Это так просто. Это секрет.
Секретный совет - ошибка Apple:
Он не будет работать только с одним элементом! Добавьте несколько ярлыков, чтобы демо-версия работала.
Вы сделали
Совет: вы должны добавить высоту для каждого нового элемента . Каждый элемент в любом представлении стека прокрутки должен иметь внутренний размер (например, метку) или добавлять явное ограничение высоты.
Альтернативный подход:
Выше: неожиданно, установите ширину UILabels равной ширине вида прокрутки (не вида стека).
С другой стороны ...
Итак, у вас есть два варианта:
или
Чтобы было ясно, используйте ОДИН из этих методов, НЕ делайте оба.
Для горизонтальной прокрутки. Сначала создайте a UIStackViewи a UIScrollViewи добавьте их в свой вид следующим образом:
let scrollView = UIScrollView()
let stackView = UIStackView()
scrollView.addSubview(stackView)
view.addSubview(scrollView)
Вспомнив , чтобы установить , translatesAutoresizingMaskIntoConstraintsчтобы falseна UIStackViewи UIScrollView:
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
Чтобы все работало, задний, ведущий, верхний и нижний якоря UIStackViewдолжны быть равны UIScrollViewякорям:
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
Но ширина якоря UIStackViewдолжна быть равна или больше ширины UIScrollViewякоря:
stackView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor).isActive = true
Теперь якорь ваш UIScrollView, например:
scrollView.heightAnchor.constraint(equalToConstant: 80).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo:view.trailingAnchor).isActive = true
Далее я бы предложил попробовать следующие параметры UIStackViewвыравнивания и распределения:
topicStackView.axis = .horizontal
topicStackView.distribution = .equalCentering
topicStackView.alignment = .center
topicStackView.spacing = 10
Наконец, вам нужно использовать addArrangedSubview:метод для добавления подпредставлений в ваш UIStackView.
Еще одна полезная функция, которая может оказаться полезной, заключается в том, что, поскольку UIStackViewона хранится внутри UIScrollView, теперь у вас есть доступ к текстовым вставкам, чтобы вещи выглядели немного красивее.
let inset:CGFloat = 20
scrollView.contentInset.left = inset
scrollView.contentInset.right = inset
// remember if you're using insets then reduce the width of your stack view to match
stackView.widthAnchor.constraint(greaterThanOrEqualTo: topicScrollView.widthAnchor, constant: -inset*2).isActive = true
Просто добавьте это к viewdidload:
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
Вы можете попробовать ScrollableStackView : https://github.com/gurhub/ScrollableStackView
Это Objective-C и Swift совместимая библиотека. Это доступно через CocoaPods .
Образец кода (Swift)
import ScrollableStackView
var scrollable = ScrollableStackView(frame: view.frame)
view.addSubview(scrollable)
// add your views with
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 55))
rectangle.backgroundColor = UIColor.blue
scrollable.stackView.addArrangedSubview(rectangle)
// ...
Образец кода (Objective-C)
@import ScrollableStackView
ScrollableStackView *scrollable = [[ScrollableStackView alloc] initWithFrame:self.view.frame];
scrollable.stackView.distribution = UIStackViewDistributionFillProportionally;
scrollable.stackView.alignment = UIStackViewAlignmentCenter;
scrollable.stackView.axis = UILayoutConstraintAxisVertical;
[self.view addSubview:scrollable];
UIView *rectangle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 55)];
[rectangle setBackgroundColor:[UIColor blueColor]];
// add your views with
[scrollable.stackView addArrangedSubview:rectangle];
// ...
Если у вас есть ограничение по центру стекового вида вертикально внутри прокрутки, просто удалите его.
Пример вертикального просмотра стека / прокрутки (с использованием EasyPeasyfor autolayout):
let scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView <- [
Edges(),
Width().like(self.view)
]
let stackView = UIStackView(arrangedSubviews: yourSubviews)
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
scrollView.addSubview(stackView)
stackView <- [
Edges(),
Width().like(self.view)
]
Просто убедитесь, что высота каждого вашего подпредставления определена!
Прежде всего, создайте свой дизайн, желательно в Sketch, или получите представление о том, что вы хотите использовать в качестве прокручиваемого контента.
После этого создайте произвольную форму контроллера представления (выберите из инспектора атрибутов) и задайте высоту и ширину в соответствии с внутренним размером содержимого вашего представления (выбирается из инспектора размера).
После этого в контроллере представления поместите представление прокрутки, и это логика, которая, как я обнаружил, работает почти все время в iOS (может потребоваться просмотр документации этого класса представления, которую можно получить с помощью команды + клик на тот класс или через гугл)
Если вы работаете с двумя или более представлениями, то сначала начните с представления, которое было представлено ранее или является более примитивным, а затем перейдите к представлению, которое было представлено позже или является более современным. Так что здесь, так как представление прокрутки было введено сначала, начните с представления прокрутки сначала и затем перейдите к представлению стека. Здесь поместите ограничения вида прокрутки в ноль во всех направлениях относительно его суперпредставления. Поместите все свои представления в это представление прокрутки и затем поместите их в представление стека.
Во время работы с представлением стека
Сначала начните с оснований вверх (подход снизу вверх), т. Е. Если у вас есть метки, текстовые поля и изображения в вашем представлении, затем сначала разложите эти представления (в представлении прокрутки), а затем поместите их в представление стека.
После этого настройте свойство стека. Если желаемый вид все еще не достигнут, тогда используйте другой вид стека.
После создания представления поместите ограничение между представлением стека матриц и представлением прокрутки, в то время как представление стека дочерних элементов ограничения будет представлением стека матриц. Надеемся, что к этому времени он будет работать нормально, или вы можете получить предупреждение от XCode с предложениями, прочитать то, что он говорит, и реализовать их. Надеюсь, теперь у вас должен быть рабочий взгляд согласно вашим ожиданиям :).
Для вложенного или одиночного стекового вида прокрутки необходимо установить фиксированную ширину с корневым представлением. Основной вид стека, который находится внутри вида прокрутки, должен иметь такую же ширину. [Мой вид прокрутки ниже представления игнорировать его]
Установите равное ограничение ширины между UIStackView и UIScrollView.
Если кто-то ищет горизонтально прокрутки
func createHorizontalStackViewsWithScroll() {
self.view.addSubview(stackScrollView)
stackScrollView.translatesAutoresizingMaskIntoConstraints = false
stackScrollView.heightAnchor.constraint(equalToConstant: 85).isActive = true
stackScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
stackScrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
stackScrollView.bottomAnchor.constraint(equalTo: visualEffectViews.topAnchor).isActive = true
stackScrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: stackScrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: stackScrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: stackScrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: stackScrollView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: stackScrollView.heightAnchor).isActive = true
stackView.distribution = .equalSpacing
stackView.spacing = 5
stackView.axis = .horizontal
stackView.alignment = .fill
for i in 0 ..< images.count {
let photoView = UIButton.init(frame: CGRect(x: 0, y: 0, width: 85, height: 85))
// set button image
photoView.translatesAutoresizingMaskIntoConstraints = false
photoView.heightAnchor.constraint(equalToConstant: photoView.frame.height).isActive = true
photoView.widthAnchor.constraint(equalToConstant: photoView.frame.width).isActive = true
stackView.addArrangedSubview(photoView)
}
stackView.setNeedsLayout()
}
Поместите вид прокрутки на вашу сцену и измените его размер так, чтобы он заполнил всю сцену. Затем поместите представление стека в представление прокрутки и поместите кнопку добавления элемента в представление стека. Как только все будет готово, установите следующие ограничения:
Scroll View.Leading = Superview.LeadingMargin
Scroll View.Trailing = Superview.TrailingMargin
Scroll View.Top = Superview.TopMargin
Bottom Layout Guide.Top = Scroll View.Bottom + 20.0
Stack View.Leading = Scroll View.Leading
Stack View.Trailing = Scroll View.Trailing
Stack View.Top = Scroll View.Top
Stack View.Bottom = Scroll View.Bottom
Stack View.Width = Scroll View.Width
Код: Stack View.Width = Scroll View.Widthэто ключ.
Добавление некоторой новой перспективы для MacOS Catalyst. Так как приложения macOS поддерживают изменение размера окна, возможно, вы UIStackViewперейдете из состояния без прокрутки в состояние с прокруткой или наоборот. Здесь есть две тонкие вещи:
UIStackView предназначен для всех областей, в которых это возможно.UIScrollViewпопытается изменить размеры своих границ, чтобы учесть вновь приобретенную / потерянную область под панелью навигации (или панелью инструментов в случае приложений MacOS).Это, к сожалению, создаст бесконечный цикл. Я не очень знаком с ним UIScrollViewи его adjustedContentInset, но из моего журнала в его layoutSubviewsметоде я вижу следующее поведение:
UIScrollView пытается уменьшить свои границы (так как нет необходимости в области под панелью инструментов).UIStackView следующим образом.UIScrollViewне удовлетворены, и решили восстановить в более широких пределах. Это очень странно для меня, так как я вижу из журнала, чтоUIScrollView.bounds.height == UIStackView.bounds.height .UIStackView следующим образом.Мне кажется, что два шага решат проблему:
UIStackView.topк UIScrollView.topMargin.contentInsetAdjustmentBehaviorв .never.Здесь меня интересует вертикально прокручиваемый вид с вертикально растущим UIStackView. Для горизонтальной пары измените код соответствующим образом.
Надеюсь, это поможет кому-нибудь в будущем. Не удалось найти никого, кто бы упомянул об этом в Интернете, и мне потребовалось довольно много времени, чтобы понять, что произошло.