Возможно ли прокручивать UIStackView?


210

Допустим, я добавил больше видов в UIStackView, которые можно отобразить, как я могу сделать UIStackViewпрокрутку?


Почему вы не используете представление прокрутки с (или несколькими) представлениями стека для контента?
Уэйн

2
Решение, состоящее из одного предложения, заключается в том, что для ширины вы перетаскиваете элемент управления в представление прокрутки дедушки, а НЕ в представление стека родителей. Объяснил в моем ответе!
Толстяк

Как встроить UIStackView в UIScrollView в iOS github.com/onmyway133/blog/issues/324
onmyway133

По поводу этого критического вопроса в iOS. На самом деле есть два (и только два) способа сделать это - это единственный способ сделать это, и вы должны точно выполнить одну из двух процедур. Мой ответ ниже всегда обновляется.
Толстяк

ДА. Вы ДОЛЖНЫ иметь промежуточный UIView в качестве представления содержимого UIScrollView. Затем поместите UIStackView в качестве дочернего представления представления содержимого. raywenderlich.com/…
poGUIst

Ответы:


578

В случае, если кто-то ищет решение без кода , я создал пример, чтобы сделать это полностью в раскадровке, используя Auto Layout.

Вы можете получить его из GitHub .

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

  1. Создайте UIScrollViewи установите его ограничения.
  2. Добавить UIStackViewкUIScrollView
  3. Установить ограничения: Leading, Trailing, Topи Bottomдолжны быть равны те изUIScrollView
  4. Установите равные Widthограничения между UIStackViewи UIScrollView.
  5. Установите Ось = Вертикаль, Выравнивание = Заливка, Распределение = Равный интервал и Интервал = 0 на UIStackView
  6. Добавить номер UIViewsвUIStackView
  7. Бегать

Замените Widthна Heightшаге 4 и установите Axis= Horizontalна шаге 5, чтобы получить горизонтальный UIStackView.


55
Спасибо! «4. Установите равное ограничение ширины между UIStackView и UIScrollView». был ключ;)
Бланк

19
Номер 6. тоже важен. Хотел добавить все представления в код, но затем Auto Layout показывает недостающие ограничения в IB.
до

11
Привет, похоже, это решение не работает для горизонтальной прокрутки. На шаге 4 вместо установки равной ширины я предполагаю установить равную высоту, а Axis = Horizontal. Я попробовал это и не работает.
Сето Элькахфи

5
Добавление ограничения, обеспечивающего одинаковую ширину стека и вида прокрутки, удалит предупреждения из компоновщика интерфейса, но тогда прокрутка будет отключена. Прокрутка происходит только тогда, когда представление содержимого внутри просмотра прокрутки больше, чем представление прокрутки!
Джейсон Мур

6
Пояснение к пункту 6. Если в представлении стека не было элементов, но все ограничения на месте, вы получаете «Ограничения вида прокрутки Необходимы для положения или высоты Y». Простое добавление метки в виде стека устраняет это предупреждение!
Faisal

45

Apple Auto Layout Guide содержит целый раздел по работе с представлениями прокрутки . Некоторые соответствующие фрагменты:

  1. Прикрепите верхний, нижний, передний и задний края представления содержимого к соответствующим краям представления прокрутки. Представление содержимого теперь определяет область содержимого представления прокрутки.
  2. (Необязательно) Чтобы отключить горизонтальную прокрутку, установите ширину представления содержимого, равную ширине представления прокрутки. Представление содержимого теперь заполняет представление прокрутки по горизонтали.
  3. (Необязательно) Чтобы отключить вертикальную прокрутку, установите высоту представления содержимого, равную высоте представления прокрутки. Представление содержимого теперь заполняет представление прокрутки по горизонтали.

Более того:

Ваш макет должен полностью определять размер представления контента (кроме случаев, определенных в шагах 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

1
Спасибо, но без ограничения высоты (чтобы обеспечить вертикальную прокрутку) выдает эту ошибку в раскадровке: Need constraints for: Y position or height.эта ошибка исчезает, только если вы устанавливаете ограничение ширины и высоты для стекового представления, которое отключает вертикальную прокрутку. Этот код все еще работает для вас?
Crashalot

@Crashalot Чтобы устранить эту ошибку, вам нужно добавить подпредставление в представление стека; см. мой ответ ниже.
pkamb

17

Ограничения в ответе с наибольшим количеством голосов здесь работали для меня, и я вставил изображение ограничений ниже, как создано в моей раскадровке.

Я столкнулся с двумя проблемами, о которых другие должны знать:

  1. После добавления ограничений, аналогичных тем, которые содержатся в принятом ответе, я получу красную ошибку автоматического определения местоположения Need constraints for: X position or width. Это было решено путем добавления UILabel в качестве подпредставления стекового представления.

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

  2. Первоначально я пытался добавить UIButtonsв UIStackView. Кнопки и представления будут загружаться, но представление прокрутки не будет прокручиваться . Это было решено путем добавленияUILabels в Stack View вместо кнопок. Используя те же ограничения, эта иерархия представления с UILabels прокручивается, а UIButton - нет.

    Я смущен этой проблемой, поскольку UIB-кнопки , кажется, имеют IntrinsicContentSize (используется стековым представлением). Если кто-нибудь знает, почему кнопки не работают, я хотел бы знать, почему.

Вот моя иерархия представления и ограничения, для справки:

ограничения для просмотра стека в представлении прокрутки [1]


3
Вы просто сэкономили мне кучу часов, врезавшихся головой в стену и разочарования, я нахожусь в точно такой же ситуации и не смог выяснить причину предупреждения и почему не работает кнопки. Почему Apple, почему !?
PakitoV

15

Как говорит Эйк, UIStackViewи UIScrollViewиграйте вместе, смотрите здесь .

Ключевым моментом является то, что он UIStackViewобрабатывает переменную высоту / ширину для различного содержимого, а UIScrollViewзатем хорошо выполняет свою работу по прокрутке / отскоку этого содержимого:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)       
}

2
Нет необходимости совмещать ручную и автоматическую разметку, чтобы обеспечить прокручиваемый вид стека. Вам просто нужно ограничить ширину и / или высоту стека. Смотрите мой ответ для деталей и ссылки на соответствующие документы Apple.
titaniumdecoy

Пример GitHub работал отлично и было очень ясно. Спасибо, что поделился.
Behr

stackView.translatesAutoresizingMaskIntoConstraints = false, это добилось цели в моем случае
Вой Дженкинс

10

Я хотел сделать то же самое и наткнулся на этот отличный пост. Если вы хотите сделать это программно с помощью 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

Пожалуйста, не добавляйте один и тот же ответ на несколько вопросов. Ответьте лучшим и отметьте остальные как дубликаты. См. Допустимо ли добавить дублирующий ответ на несколько вопросов?
FelixSFD

10
Они не совсем дубликаты в этом случае. Другой вопрос конкретно задает, как это сделать программно, а этот - нет. Действительно, лучший ответ на этот вопрос обеспечивает метод GUI для достижения результата. Тем не менее, программное решение может быть полезным для некоторых пользователей.
Далмацио

9

В актуальном состоянии до 2020 года.

100% раскадровка ИЛИ 100% код.


Вот самое простое объяснение:

  1. Пустая полноэкранная сцена

  2. Добавить вид прокрутки. Удерживая клавишу Control, перетащите из вида прокрутки в базовый вид , добавьте левый-правый-верхний-нижний, все ноль.

  3. Добавьте представление стека в представлении прокрутки. Удерживая клавишу Control, перетащите из представления стека в представление прокрутки , добавьте левый-правый-верхний-нижний, все ноль.

  4. Поместите две или три метки внутри стека.

Для наглядности сделайте цвет фона метки красным. Установите высоту этикетки до 100.

  1. Теперь установите ширину каждой UILabel:

    Удивительно, но перетаскиванием элемента управления из в UILabelпредставление прокрутки, а не в представление стека, и выберите равную ширину .

Повторить:

Не контролируйте перетаскивание от UILabel к родителю UILabel - переходите к бабушке и дедушке. (Другими словами, перейдите полностью к представлению прокрутки, не переходите к представлению стека.)

Это так просто. Это секрет.

Секретный совет - ошибка Apple:

Он не будет работать только с одним элементом! Добавьте несколько ярлыков, чтобы демо-версия работала.

Вы сделали

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


Альтернативный подход:

Выше: неожиданно, установите ширину UILabels равной ширине вида прокрутки (не вида стека).

С другой стороны ...

Перетащите из представления стека в представление прокрутки и добавьте ограничение "ширина равна". Это кажется странным, потому что вы уже прикололись влево-вправо , но именно так вы это делаете . Неважно, как странно, что это секрет.

Итак, у вас есть два варианта:

  1. Удивительно, но установите ширину каждого элемента в представлении стека равной ширине прародителя scrollview ( не родительского стека ).

или

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

Чтобы было ясно, используйте ОДИН из этих методов, НЕ делайте оба.


8

Горизонтальная прокрутка (UIStackView в UIScrollView)

Для горизонтальной прокрутки. Сначала создайте 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

7

Просто добавьте это к viewdidload:

let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets

источник: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html


Не могу сказать, насколько это спасло мои проблемы с Autolayout + ScrollView. Отличная находка по той ссылке Apple.
DED

что делает? Что происходит без этого?
Мед

Это только размещает просмотр прокрутки контента ниже строки состояния.
Тестирование проводится

2

Вы можете попробовать 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]; 
// ...

Привет Питер, на самом деле отличная идея! Вклады всегда приветствуются, пожалуйста, не стесняйтесь отправить запрос на это. Лучший.
Mgyky

0

Если у вас есть ограничение по центру стекового вида вертикально внутри прокрутки, просто удалите его.


0

Пример вертикального просмотра стека / прокрутки (с использованием 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)
]

Просто убедитесь, что высота каждого вашего подпредставления определена!


0
  1. Прежде всего, создайте свой дизайн, желательно в Sketch, или получите представление о том, что вы хотите использовать в качестве прокручиваемого контента.

  2. После этого создайте произвольную форму контроллера представления (выберите из инспектора атрибутов) и задайте высоту и ширину в соответствии с внутренним размером содержимого вашего представления (выбирается из инспектора размера).

  3. После этого в контроллере представления поместите представление прокрутки, и это логика, которая, как я обнаружил, работает почти все время в iOS (может потребоваться просмотр документации этого класса представления, которую можно получить с помощью команды + клик на тот класс или через гугл)

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

Во время работы с представлением стека

  • Сначала начните с оснований вверх (подход снизу вверх), т. Е. Если у вас есть метки, текстовые поля и изображения в вашем представлении, затем сначала разложите эти представления (в представлении прокрутки), а затем поместите их в представление стека.

  • После этого настройте свойство стека. Если желаемый вид все еще не достигнут, тогда используйте другой вид стека.

  • Если все еще не достигнуто, поиграйте с сопротивлением сжатию или приоритетом объятия контента.
  • После этого добавьте ограничения в представление стека.
  • Также подумайте об использовании пустого UIView в качестве заполнителя, если все вышеперечисленное не дает удовлетворительных результатов.

После создания представления поместите ограничение между представлением стека матриц и представлением прокрутки, в то время как представление стека дочерних элементов ограничения будет представлением стека матриц. Надеемся, что к этому времени он будет работать нормально, или вы можете получить предупреждение от XCode с предложениями, прочитать то, что он говорит, и реализовать их. Надеюсь, теперь у вас должен быть рабочий взгляд согласно вашим ожиданиям :).


0

Для вложенного или одиночного стекового вида прокрутки необходимо установить фиксированную ширину с корневым представлением. Основной вид стека, который находится внутри вида прокрутки, должен иметь такую ​​же ширину. [Мой вид прокрутки ниже представления игнорировать его]

Установите равное ограничение ширины между UIStackView и UIScrollView.

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


0

Если кто-то ищет горизонтально прокрутки

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()

 }

0

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

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это ключ.


0

Добавление некоторой новой перспективы для MacOS Catalyst. Так как приложения macOS поддерживают изменение размера окна, возможно, вы UIStackViewперейдете из состояния без прокрутки в состояние с прокруткой или наоборот. Здесь есть две тонкие вещи:

  • UIStackView предназначен для всех областей, в которых это возможно.
  • Во время перехода UIScrollViewпопытается изменить размеры своих границ, чтобы учесть вновь приобретенную / потерянную область под панелью навигации (или панелью инструментов в случае приложений MacOS).

Это, к сожалению, создаст бесконечный цикл. Я не очень знаком с ним UIScrollViewи его adjustedContentInset, но из моего журнала в его layoutSubviewsметоде я вижу следующее поведение:

  • Один увеличивает окно.
  • UIScrollView пытается уменьшить свои границы (так как нет необходимости в области под панелью инструментов).
  • UIStackView следующим образом.
  • Как-то UIScrollViewне удовлетворены, и решили восстановить в более широких пределах. Это очень странно для меня, так как я вижу из журнала, чтоUIScrollView.bounds.height == UIStackView.bounds.height .
  • UIStackView следующим образом.
  • Затем перейдите к шагу 2.

Мне кажется, что два шага решат проблему:

  • Align UIStackView.topк UIScrollView.topMargin.
  • Установите contentInsetAdjustmentBehaviorв .never.

Здесь меня интересует вертикально прокручиваемый вид с вертикально растущим UIStackView. Для горизонтальной пары измените код соответствующим образом.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.