UIStackView “Неможливо одночасно задовольнити обмеження” для “скривлених” прихованих подань


95

Коли мої "рядки" UIStackView стискаються, вони видають AutoLayoutпопередження. Однак вони відображаються чудово, і нічого іншого не помиляється, крім таких видів реєстрації:

Неможливо одночасно задовольнити обмеження. Можливо, принаймні одне з обмежень у наведеному нижче списку - це одне, яке ви не хочете. Спробуйте наступне: (1) розгляньте кожне обмеження та спробуйте зрозуміти, чого ви не очікуєте; (2) знайти код, який додав небажане обмеження або обмеження, і виправити його. (Примітка. Якщо ви бачите, NSAutoresizingMaskLayoutConstraintsщо не розумієте, зверніться до документації до UIViewвласності translatesAutoresizingMaskIntoConstraints) (

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

Хтось знає, як це вирішити? Цікаво, що обмеження макету досить часто позначаються як "приховування UISV" , що вказує на те, що, можливо, це повинно ігнорувати мінімуми висоти для підпроектів або щось у цьому випадку?


1
Здається, це виправлено в iOS11, і тут не отримували жодних попереджень
траппер

Відповіді:


204

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

Я отримав таку помилку:

2015-10-01 11:45:13.732 <redacted>[64455:6368084] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5be508d0 V:|-(8)-[UISegmentedControl:0x7f7f5bec4180]   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5bdfbda0 'UISV-hiding' V:[UIView:0x7f7f5be69d30(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Те, що я намагався зробити, - це розмістити UIViewв моєму приміщенні, UIStackViewщо містило UISegmentedControlврізку на 8 пунктів з кожного краю.

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

Щоб вирішити проблему, я змінив 8pt зверху нижній пріоритет нижнього обмеження з 1000 на 999, так що UISV-hidingобмеження може мати пріоритет, якщо це необхідно.


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

4
Зміна пріоритетів також спрацювала для мене. Крім того, видалення зайвих (затемнених) обмежень, випадково скопійованих із невикористаних класів розміру. ВАЖЛИВА ПОРАДА: щоб легше налагоджувати ці проблеми, встановіть рядок IDENTIFER на кожному обмеженні. Тоді ви зможете побачити, яке обмеження було неслухняним у повідомленні про налагодження.
Womble

3
У моєму випадку мені потрібно лише знизити пріоритет по висоті, і це працює.
pixelfreak

Ця підказка IDENTIFIER чудова! Я завжди дивувався, як вказати обмеження в іменах повідомлень про налагодження, я завжди прагнув додати щось у подання, а не саме обмеження. Дякую @Womble!
Райан,

Дякую! Пріоритет від 1000 до 999 зробив трюк. Xcode: Версія 8.3.3 (8E3004b)
Майкл Гаріто

52

У мене була подібна проблема, яку було нелегко вирішити. У моєму випадку у мене був вид стека, вбудований у вид стека. Внутрішній UIStackView мав дві мітки та ненульовий інтервал.

Коли ви викликаєте addArrangedSubview (), він автоматично створює обмеження, подібні до таких:

V:|[innerStackView]|              | = outerStackView

  V:|[label1]-(2)-[label2]|       | = innerStackView

Тепер, коли ви намагаєтеся приховати innerStackView, ви отримуєте неоднозначне попередження про обмеження.

Щоб зрозуміти чому, давайте спочатку подивимося, чому цього не відбувається, коли innerStackView.spacingдорівнює 0. Коли ви телефонуєте innerStackView.hidden = true, @liamnichols був правильним ... outerStackViewвін чарівно перехопить цей дзвінок і створить обмеження 0висоти, що приховує UISV, з пріоритетом 1000 (обов'язково). Імовірно, це дозволить анімувати елементи у поданні стека поза зором, якщо ваш приховуючий код викликається всередині UIView.animationWithDuration()блоку. На жаль, здається, немає способу запобігти додаванню цього обмеження. Тим не менше, ви не отримаєте попередження "Неможливо одночасно задовольнити обмеження" (USSC), оскільки трапляється таке:

  1. Висота label1 встановлена ​​на 0
  2. інтервал між двома мітками вже визначався як 0
  3. Висота label2 встановлена ​​на 0
  4. висота innerStackView встановлена ​​на 0

Зрозуміло, що ці 4 обмеження можна задовольнити. Вигляд стека просто згладжує все в піксель висоти 0.

Тепер, повертаючись до баггі , наприклад, якщо ми встановлюємо , spacingщоб 2ми тепер маємо такі обмеження:

  1. Висота label1 встановлена ​​на 0
  2. інтервал між двома мітками автоматично створювався в режимі стека як 2 пікселі з пріоритетом 1000.
  3. Висота label2 встановлена ​​на 0
  4. висота innerStackView встановлена ​​на 0

Вигляд стека не може мати висоту 0 пікселів, а вміст - 2 пікселі. Обмеження неможливо задовольнити.

Примітка: Ви можете побачити таку поведінку на більш простому прикладі. Просто додайте UIView до подання стека як упорядкований підпрогляд. Потім встановіть обмеження висоти для цього UIView з пріоритетом 1000. Тепер спробуйте зателефонувати сховати на це.

Примітка: З будь-якої причини це сталося лише тоді, коли моє подання стека було підпрограмою UICollectionViewCell або UITableViewCell. Однак ви все одно можете відтворити цю поведінку поза комірки, викликавши innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)наступний цикл запуску після приховування внутрішнього перегляду стека.

Примітка: Навіть якщо ви спробуєте виконати код у UIView.performWithoutAnimations, подання стека все одно додасть обмеження висоти 0, що спричинить попередження USSC.


Існує щонайменше 3 рішення цієї проблеми:

  1. Перш ніж приховувати будь-який елемент у поданні стека, перевірте, чи це вигляд стека, і якщо так, то змініть значення spacingна 0. Це дратує, оскільки вам потрібно змінити процес (і пам’ятати початковий інтервал), коли ви знову показуєте вміст.
  2. Замість того, щоб приховувати елементи у поданні стека, телефонуйте removeFromSuperview. Це ще більше дратує, оскільки коли ви змінюєте процес назад, вам потрібно пам’ятати, куди вставити вилучений елемент. Ви можете оптимізувати, лише зателефонувавши removeArrangedSubview, а потім приховавши, але є багато бухгалтерії, яку ще потрібно зробити.
  3. Оберніть вкладені подання стека (які мають ненульове значення spacing) в UIView. Вкажіть принаймні одне обмеження як необов’язковий пріоритет (999 або нижче). Це найкраще рішення, оскільки вам не потрібно займатися веденням бухгалтерії. У моєму прикладі я створив верхні, ведучі та кінцеві обмеження на 1000 між видом стека та видом обгортки, а потім створив обмеження 999 від нижньої частини стека до подання обгортки. Таким чином, коли зовнішній вигляд стека створює обмеження нульової висоти, обмеження 999 порушується, і ви не бачите попередження USSC. (Примітка: Це схоже на рішення для Якщо для contentView.translatesAutoResizingMaskToConstraints підкласу UICollectionViewCell встановлено значенняfalse )

Таким чином, причиною такої поведінки є:

  1. Apple автоматично створює для вас 1000 пріоритетних обмежень, коли ви додаєте керовані підпрогляди до подання стека.
  2. Apple автоматично створює для вас обмеження висоти 0, коли ви приховуєте підпрогляд подання стека.

Якби Apple (1) дозволила вам вказати пріоритет обмежень (особливо розпірок), або (2) дозволила вам відмовитись від автоматичного обмеження приховування UISV , цю проблему було б легко вирішити.


6
Дякуємо за надзвичайно ретельне та корисне пояснення. Однак це, безумовно, здається помилкою на стороні Apple із переглядами стека. В основному їхня функція "приховування" несумісна з функцією "відстані". Будь-які ідеї, які вони вирішили з тих пір, або додали деякі функції для запобігання злому додатковими переглядами? (Знову ж таки, велика розбивка потенційних рішень і погоджуємось на елегантність №3)
березня 2016 р.,

1
Чи потрібно кожній UIStackViewдитині, яка UIStackViewпотребує обгортання, UIViewабо просто тій, яку ви хочете приховати / показати?
Адріан,

1
Це здається справді поганим недоглядом з боку Apple. Особливо тому, що попередження та помилки, пов’язані з використанням, UIStackViewяк правило, загадкові та важкі для розуміння.
bompf

Це рятівник. У мене була проблема, коли UIStackViews, доданий до UITableViewCell, спричиняв спам журналу помилок AutoLayout, кожного разу, коли клітинку використовували повторно. Вбудовування stackView в UIView із встановленим для низького пріоритету обмеження прив'язки низьким, вирішило проблему. Це змушує налагоджувач подання відображати елементи stackView як неоднозначні висоти, але він відображається належним чином у програмі, без спаму в журналі помилок. СПАСИБІ.
Womble

6

Здебільшого цю помилку можна вирішити, знизивши пріоритет обмежень, щоб усунути конфлікти.


Що ви маєте на увазі? Всі обмеження відносні у переглядах, що складені ...
Бен Гільдія,

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

Здається, це відбувається з того моменту, коли речі роздавлюються або коли їх демонструють / приховують. У цьому випадку він стає частково видимим. - Можливо, потрібно насправді пройти і виключити будь-які мінімальні вертикальні константи, тому що їх можна стиснути до 0 висоти?
Ben Guild

2

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

Однак якщо ви не дбаєте про анімацію (можливо, ви приховуєте її в ViewDidLoad), тоді ви можете просто, removeFromSuperviewяка матиме той самий ефект, але без будь-яких проблем з обмеженнями, оскільки вони будуть видалені разом із видом.


1

На основі відповіді @ Senseful, ось розширення UIStackView для обгортання подання стека у поданні та застосування обмежень, які він або вона рекомендує:

/// wraps in a `UIView` to prevent autolayout warnings when a stack view with spacing is placed inside another stack view whose height might be zero (usually due to `hidden` being `true`).
/// See http://stackoverflow.com/questions/32428210
func wrapped() -> UIView {
    let wrapper = UIView()
    translatesAutoresizingMaskIntoConstraints = false
    wrapper.addSubview(self)

    for attribute in [NSLayoutAttribute.Top, .Left, .Right, .Bottom] {
        let constraint = NSLayoutConstraint(item: self,
                                            attribute: attribute,
                                            relatedBy: .Equal,
                                            toItem: wrapper,
                                            attribute: attribute,
                                            multiplier: 1,
                                            constant: 0)
        if attribute == .Bottom { constraint.priority = 999 }
        wrapper.addConstraint(constraint)
    }
    return wrapper
}

Замість того, щоб додавати свій stackView, використовуйте stackView.wrapped().


1

По-перше, як пропонували інші, переконайтеся, що обмеження, якими ви можете керувати, тобто не обмеження, властиві UIStackView, встановлені в пріоритет 999, щоб вони могли бути замінені, коли подання приховано.

Якщо проблема все ще виникає, проблема, ймовірно, пов’язана з інтервалом у прихованих StackViews. Моє рішення полягало в тому, щоб додати UIView як пробіл і встановити нульовий інтервал UIStackView. Потім встановіть обмеження View.height або View.width (залежно від вертикального або горизонтального стека) на відстань між StackView.

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

Все вищесказане можна зробити в Interface Builder. Можливо, вам доведеться програмно приховати / показати деякі нещодавно додані подання, щоб у вас не було небажаного інтервалу.


1

Нещодавно я боровся з помилками автоматичної розкладки, приховуючи файл UIStackView. Замість того, щоб робити купу книжок для ведення книг та загортання UIViews, я вирішив створити торгову точку для своїх parentStackViewта торгові точки для дітей, яких хочу сховати / показати.

@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!

У розкадровці ось як виглядає my parentStack:

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

У ньому 4 дитини, і у кожного з них є купа виглядів стека всередині них. Якщо ви приховуєте подання стека, якщо в ньому є елементи інтерфейсу, які також є переглядами стека, ви побачите потік помилок автоматичного розміщення. Замість того, щоб приховувати, я вирішив їх видалити.

У моєму прикладі parentStackViewsмістить масив з 4 елементів: Вид зверху стека, StackViewNumber1, номер стека номер 2 і кнопка зупинки. Їх показники arrangedSubviewsскладають 0, 1, 2 та 3 відповідно. Коли я хочу сховати один, я просто видаляю його з parentStackView's arrangedSubviewsмасиву. Оскільки він не слабкий, він залишається в пам'яті, і ви можете просто повернути його назад до бажаного індексу пізніше. Я не повторно ініціалізую його, тому він просто висить, доки це не потрібно, але не здуває пам’ять.

Отже, ви можете ...

1) Перетягніть IBOutlets для вашого батьківського стеку та дітей, яких ви хочете сховати / показати, на розкадрування.

2) Коли ви хочете їх приховати, видаліть стек, який ви хочете сховати, з parentStackView's arrangedSubviewsмасиву.

3) Телефонуйте за self.view.layoutIfNeeded()допомогою UIView.animateWithDuration.

Зверніть увагу, що останні два stackViews не є weak. Вам потрібно тримати їх поруч, коли ви їх показуєте.

Скажімо, я хочу приховати stackViewNumber2:

parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()

Потім анімуйте це:

UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

Якщо ви хочете "показати" stackViewNumber2пізніше, ви можете просто вставити його у потрібний parentStackView arrangedSubViewsіндекс і анімувати оновлення.

parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)

// Then animate it
UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

Я виявив, що набагато простіше, ніж вести бухгалтерію з обмеженнями, возитися з пріоритетами тощо.

Якщо у вас є щось, що ви хочете приховати за замовчуванням, ви можете просто викласти це на розкадруванні та видалити його viewDidLoadта оновити без використання анімації view.layoutIfNeeded().


1

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

Я вирішив помилки обмеження, сховавши спочатку всі подання стека (налаштування isHidden = true), перш ніж приховувати батьківський стек.

Виконання цього не мало всієї складності видалення допоміжних упорядкованих подань, підтримання індексу при необхідності їх додавання назад.

Сподіваюся, це допомагає.


1

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

Все, що вам потрібно зробити, це встановити пріоритет усіх обмежень stackView нижче 1000 (999 виконає роботу). Наприклад, якщо stackView обмежений ліворуч, праворуч, зверху та знизу до свого перегляду, тоді всі 4 обмеження повинні мати пріоритет нижче 1000.


0

Можливо, ви створили обмеження під час роботи з певним класом розміру (наприклад: wCompact hRegular), а потім створили дублікат при переході на інший клас розміру (наприклад: wAny hAny). перевірте обмеження об'єктів інтерфейсу в різних класах розміру та перевірте, чи є аномалії з обмеженнями. ви повинні побачити червоні лінії, що вказують на обмеження, що стикаються. Я не можу поставити малюнок, поки не отримаю 10 балів за репутацію вибачте: /


О, добре. але я отримав цю помилку, коли в мене був випадок, який я описав drive.google.com/file/d/0B-mn7bZcNqJMVkt0OXVLVVdnNTA/…
FM

Так, я точно не бачу жодного червоного, перемикаючи класи розмірів у конструкторі інтерфейсів. Я використовував лише розмір "Будь-який".
Ben Guild

0

Я хотів приховати цілі UIStackView за раз, але я отримував ті самі помилки, що і OP, це мені це виправило:

for(UIView *currentView in self.arrangedSubviews){
    for(NSLayoutConstraint *currentConstraint in currentView.constraints){
        [currentConstraint setPriority:999];
    }
}

Це не спрацювало для мене, оскільки механізм автоматичного розміщення скаржиться, коли ви змінюєте необхідне обмеження ( priority = 1000) на не потрібне ( priority <= 999).
Почутливий

0

У мене був ряд кнопок з обмеженням висоти. Це трапляється, коли одна кнопка прихована. Встановлення пріоритету обмеження висоти кнопок на 999 вирішило проблему.


-2

Ця помилка не має нічого спільного з UIStackView. Це трапляється, коли у вас є обмеження конфліктів з однаковими пріоритетами. Наприклад, якщо у вас обмеження стверджує, що ширина вашого подання дорівнює 100, а у вас є інше обмеження, одночасно стверджує, що ширина подання становить 25% його контейнера. Очевидно, що існують два суперечливі обмеження. Рішення - видалити їх.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.