Чи можливо для прокрутки UIStackView?


210

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


Чому ви не використовуєте подання прокрутки з (або декількома) переглядами стеку для вмісту?
Вейн

2
Рішенням з одним реченням є те, що для ширини ви перетягуєте до перегляду прокрутки бабусі та дідуся, а не перегляду батьківського стека. Пояснив у своїй відповіді!
Fattie

Як вставити UIStackView всередині UIScrollView в iOS github.com/onmyway133/blog/isissue/324
onmyway133

Щодо цього критичного питання в iOS. Насправді є два (і лише два) способи зробити це - це єдиний спосіб зробити це, і ви повинні точно дотримуватися одну з двох процедур. Моя відповідь нижче завжди оновлюється.
Fattie

ТАК. У вас ОБОВ'ЯЗКОВО мати проміжний UIView як перегляд вмісту UIScrollView. Потім розмістіть UIStackView як дочірній вигляд перегляду вмісту. raywenderlich.com/…
poGUIst

Відповіді:


577

У випадку, якщо хтось шукає рішення без коду , я створив приклад, щоб це зробити повністю на дошці розкадрування, використовуючи функцію автоматичного макета.

Ви можете отримати його з 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. також важливе. Хотіли додати всі коди в код, але тоді в автоматичному макеті відображаються відсутні контрацепти в ІБ.
до

11
Вітаю, здається, що це рішення не працює для горизонтальної прокрутки. На кроці 4 замість заданої однакової ширини я припускаю встановити рівну висоту, а ось - горизонтальний. Я спробував це і не виходить.
Сето Елкафіфі

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

6
Щоб уточнити точку 6. Якщо у представництві стека не було елементів, але всі обмеження є на місці, ви отримаєте "Перегляд прокрутки Потрібні обмеження для положення Y або висоти". Просто додавання мітки у подання стеку це попередження відключається!
Файсал Мемон

45

Посібник з автоматичного розміщення Apple містить цілий розділ щодо роботи з видами прокрутки . Деякі відповідні фрагменти:

  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 Щоб усунути цю помилку, потрібно додати підпогляд до подання стека; дивіться мою відповідь нижче.
пкамб

16

Обмеження у відповіді, що голосує тут, спрацювали для мене, і я вставив зображення обмежень нижче, як це було створено в моїй розгортці.

Я торкнувся двох питань, хоча інші повинні знати:

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

    Я додаю підпогляди програмно, тож у мене спочатку не було підглядів на дошці розкадрувань. Щоб позбутися від помилок автоматичного розкладу, додайте підрозділ до дошці розкадрування, після чого видаліть його під час завантаження, перш ніж додавати реальні підгляди та обмеження.

  2. Я спочатку намагався додати UIButtonsдо UIStackView. Кнопки та види завантажуватимуться, але подання прокрутки не прокручується . Це було вирішено шляхом додавання UILabelsдо перегляду стека замість кнопок. Використовуючи ті самі обмеження, ця ієрархія перегляду з UILabels прокручується, але кнопки UIB не мають.

    Мене бентежить це питання, оскільки , здається, UIButtons має 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 працював чудово і був дуже чітким. Дякую, що поділились.
Бер

stackView.translatesAutoresizingMaskIntoConstraints = false, це зробило трюк у моєму випадку
Howl Jenkins

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

  3. Додайте подання стека у вікно прокрутки. Перетягніть контроль із перегляду стека до подання прокрутки , додайте ліво-право-вгорі-вниз, усі нулі.

  4. Поставте дві або три мітки всередині подання стека.

Для наочності зробіть колір тла етикетки червоним. Встановіть висоту етикетки на 100.

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

    Дивно, але перетягніть курсор від UILabelперегляду прокрутки, а не до виду стека, і виберіть однакові ширини .

Повторювати:

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

Це так просто. У цьому секрет.

Підказка - помилка Apple:

Він не працюватиме лише з одним предметом! Додайте кілька міток, щоб демонстрація працювала.

Ви закінчили.

Порада. Кожному новому елементу потрібно додати висоту . Кожен елемент у будь-якому перегляді стека прокрутки повинен мати або внутрішній розмір (наприклад, мітка), або додавати явне обмеження по висоті.


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

У вищесказаному: напрочуд, встановіть ширину міток UIL на ширину подання прокрутки (не вигляд стека).

По черзі ...

Перетягніть з подання стека до подання прокрутки та додайте обмеження "рівна ширина". Це здається дивним, оскільки ви вже прикололи ліво-право , але це так, як ви це робите . Як би дивно це не здавалося, у цьому секрет.

Отже, у вас є два варіанти:

  1. Дивно, але встановіть ширину кожного елемента в поданні стека на ширину прокрутки бабусі-дідуся (а не батьківського сценарію ).

або

  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.
дед

що робить? Що відбувається без цього?
Мед

Це розміщує лише вміст перегляду прокрутки під рядком стану.
turingtest

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

Приклад вертикального перегляду стека / прокрутки (використовуючи EasyPeasyдля автоматичного вимикання):

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. Перш за все, спроектуйте свій погляд, бажано на щось на зразок ескізу або отримайте уявлення про те, що ви хочете як вміст, який можна прокручувати.

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

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

    Якщо ви працюєте з двома або більше переглядами, то спочатку почніть з представлення, яке було введено раніше або є більш примітивним, а потім перейдіть до представлення, яке було введено пізніше або є більш сучасним. Отже, оскільки спочатку було запроваджено перегляд прокрутки, спочатку почніть з подання прокрутки, а потім перейдіть до подання стека. Тут встановіть обмеження перегляду прокрутки до нуля в усіх напрямках відносно його суперзору. Помістіть усі перегляди всередині цього вікна прокрутки, а потім поставте їх у подання стека.

Працюючи з поданням стека

  • Спочатку почніть з основи (підхід знизу), тобто, якщо у вас є мітки, текстові поля та зображення, то спочатку викладіть ці подання (всередині вікна прокрутки), а потім поставте їх у подання стека.

  • Після цього налаштувати властивість подання стека. Якщо бажаного перегляду все ще не досягнуто, використовуйте інший вигляд стека.

  • Якщо цього не вдалося досягти, то грайте з опором стиснення або пріоритетом обіймання вмісту.
  • Після цього додайте обмеження до подання стека.
  • Також подумайте про використання порожнього 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.