UIStackView Невозможно одновременно удовлетворить ограничения на сжатые скрытые представления

Когда мои "строки" UIStackView сжимаются, они выдают AutoLayout предупреждение. Тем не менее, они отображаются нормально, и ничего плохого, кроме этих видов журналов:

Невозможно одновременно удовлетворить ограничения. Вероятно, по крайней мере, одно из ограничений в следующем списке вам не нужно. Попробуйте следующее: (1) посмотрите на каждое ограничение и попытайтесь выяснить, чего вы не ожидаете; (2) найдите код, который добавил нежелательное ограничение или ограничения, и исправьте его. (Примечание: если вы видите NSAutoresizingMaskLayoutConstraints, чего не понимаете, обратитесь к документации по свойству UIView translatesAutoresizingMaskIntoConstraints) (

Итак, я еще не уверен, как это исправить, но, похоже, это ничего не ломает, кроме того, что просто раздражает.

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


person Ben Guild    schedule 06.09.2015    source источник
comment
Кажется, это исправлено в iOS11, здесь не появляются предупреждения.   -  person trapper    schedule 15.05.2018


Ответы (14)


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

person liamnichols    schedule 01.10.2015
comment
Следует отметить, что, похоже, если у вас есть ограничение по высоте или ширине для них и уменьшите их приоритет, это не сработает. Вам нужно удалить высоту / ширину и добавить верхние ведущие конечные основания, затем установить их приоритет как более низкий, и он работает. - person bolnad; 10.05.2016
comment
Смена приоритетов тоже сработала. Кроме того, удаление любых лишних (затененных) ограничений, которые были случайно скопированы из неиспользуемых классов размеров. ВАЖНЫЙ СОВЕТ: чтобы упростить отладку этих проблем, установите строку IDENTIFER для каждого ограничения. Затем вы можете увидеть, какое ограничение было непослушным в отладочном сообщении. - person Womble; 02.06.2016
comment
В моем случае мне нужно только снизить приоритет по высоте, и это работает. - person pixelfreak; 24.07.2016
comment
Совет IDENTIFIER великолепен! Мне всегда было интересно, как задать ограничения в именах отладочных сообщений, я всегда хотел добавить что-то в представление, а не само ограничение. Спасибо, @Womble! - person Ryan; 12.05.2017
comment
Спасибо! Приоритет от 1000 до 999 помог. Xcode: Версия 8.3.3 (8E3004b) - person Michael Garito; 27.08.2017
comment
Также происходит на iOS 11 :(. Понижение приоритета сработало, но не уверен, почему это происходит для меня только внутри ячейки. - person GoodSp33d; 04.12.2017

У меня была аналогичная проблема, которую было нелегко решить. В моем случае у меня было представление стека, встроенное в представление стека. Для внутреннего 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 ограничения могут быть удовлетворены. Представление стека просто превращает все в пиксель нулевой высоты.

Теперь вернемся к примеру с ошибкой, если мы установим 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-скрытия, эта проблема была бы легко устранена. решено.

person Senseful    schedule 27.06.2016
comment
Спасибо за очень подробное и полезное объяснение. Однако это определенно кажется ошибкой со стороны Apple с представлениями стека. В основном их функция сокрытия несовместима с их функцией интервала. Есть ли идеи, что они решили эту проблему с тех пор или добавили некоторые функции, чтобы предотвратить взлом с дополнительными содержащими представлениями? (Опять же, большое количество потенциальных решений и согласен с элегантностью # 3) - person Marchy; 25.08.2016
comment
Нужно ли обернуть каждый отдельныйUIStackView дочерний элемент UIStackView в UIView или только тот, который вы хотите скрыть / показать? - person Adrian; 17.03.2017
comment
Похоже, что со стороны Apple это действительно плохой надзор. Тем более, что предупреждения и ошибки, связанные с использованием UIStackView, обычно загадочны и трудны для понимания. - person bompf; 25.06.2018
comment
Это палочка-выручалочка. У меня возникла проблема, когда UIStackViews, добавленные в UITableViewCell, вызывали спам в журнале ошибок AutoLayout каждый раз, когда ячейка использовалась повторно. Встраивание stackView в UIView с низким приоритетом ограничения нижнего якоря решило проблему. Это заставляет отладчик представления отображать элементы stackView как имеющие неоднозначную высоту, но он отображается правильно в приложении без спама в журнале ошибок. СПАСИБО. - person Womble; 17.05.2020

В большинстве случаев эту ошибку можно устранить, снизив приоритет ограничений, чтобы устранить конфликты.

person Luciano Almeida    schedule 06.09.2015
comment
Что ты имеешь в виду? Все ограничения являются относительными в пределах представлений, которые сложены в стек .... - person Ben Guild; 07.09.2015
comment
Извините, я не понимаю ваш вопрос правильно, мой английский таков, но я думаю, что ограничения связаны с представлением, с которым оно связано, если представление становится скрытым, ограничения становятся неактивными. Не уверен, что вы сомневаетесь в этом, но надеюсь, что смогу помочь. - person Luciano Almeida; 07.09.2015
comment
Кажется, что это происходит, когда материал сжимается или находится в процессе отображения / скрытия. В этом случае он становится частично видимым. - Может быть, действительно необходимо пройти и исключить любые минимальные вертикальные постоянные, потому что их можно сжать до нулевой высоты? - person Ben Guild; 07.09.2015

Когда вы устанавливаете вид как скрытый, UIStackview будет пытаться отменить его анимацию. Если вам нужен такой эффект, вам нужно установить правильный приоритет для ограничений, чтобы они не конфликтовали (как многие предлагали выше).

Однако, если вам не нужна анимация (возможно, вы скрываете ее в ViewDidLoad), вы можете просто removeFromSuperview, который будет иметь тот же эффект, но без каких-либо проблем с ограничениями, поскольку они будут удалены вместе с представлением.

person Oren    schedule 05.09.2016

Основываясь на ответе @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().

person Ben Packard    schedule 04.08.2016

Во-первых, как предлагали другие, убедитесь, что ограничения, которыми вы можете управлять, т.е. не ограничения, присущие UIStackView, имеют приоритет 999, чтобы их можно было переопределить, когда представление скрыто.

Если проблема все еще не устранена, проблема, скорее всего, связана с интервалом в скрытых StackViews. Мое решение заключалось в том, чтобы добавить UIView в качестве разделителя и установить для UIStackView интервал равным нулю. Затем установите ограничения View.height или View.width (в зависимости от вертикального или горизонтального стека) на интервал StackView. .

Затем отрегулируйте приоритеты объятия контента и сопротивления сжатию контента ваших недавно добавленных представлений. Возможно, вам также придется изменить распределение родительского StackView.

Все вышеперечисленное можно сделать в Интерфейсном Разработчике. Вам также может потребоваться программно скрыть / отобразить некоторые из недавно добавленных представлений, чтобы у вас не было нежелательных интервалов.

person Peter Coyle    schedule 11.09.2016

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

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

Вот как выглядит мой parentStack в раскадровке:

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

У него 4 дочерних элемента, и каждый из них имеет внутри несколько видов стека. Когда вы скрываете представление стека, если у него есть элементы пользовательского интерфейса, которые также являются представлениями стека, вы увидите поток ошибок автоматического макета. Вместо того, чтобы прятаться, я решил удалить их.

В моем примере parentStackViews содержит массив из 4 элементов: Top Stack View, StackViewNumber1, Stack View Number 2 и Stop Button. Их индексы в arrangedSubviews равны 0, 1, 2 и 3 соответственно. Когда я хочу скрыть один, я просто удаляю его из массива parentStackView's arrangedSubviews. Поскольку он неслабый, он задерживается в памяти, и вы можете просто вернуть его по желаемому индексу позже. Я не инициализирую его повторно, поэтому он просто зависает, пока не понадобится, но не раздувает память.

В общем, вы можете ...

1) Перетащите IBOutlets для родительского стека и дочерних элементов, которых вы хотите скрыть / показать, на раскадровку.

2) Если вы хотите их скрыть, удалите стек, который вы хотите скрыть, из массива parentStackView's arrangedSubviews.

3) Позвоните self.view.layoutIfNeeded() с помощью UIView.animateWithDuration.

Обратите внимание, что последние два stackView не 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().

person Adrian    schedule 17.03.2017

У меня были те же ошибки со встроенными представлениями стека, хотя во время выполнения все работало нормально.

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

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

Надеюсь это поможет.

person Will Stevens    schedule 23.04.2017

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

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

person Linh Ta    schedule 25.02.2019

Возможно, вы создали ограничение при работе с определенным классом размера (например, wCompact hRegular), а затем создали дубликат при переключении на другой класс размера (например, wAny hAny). проверьте ограничения объектов пользовательского интерфейса в разных классах размера и посмотрите, есть ли аномалии с ограничениями. вы должны увидеть красные линии, указывающие на ограничения столкновения. Я не могу поставить картинку, пока не наберу 10 очков репутации, извините: /

person F.M    schedule 06.09.2015
comment
Ох, хорошо. но я получил эту ошибку, когда у меня был случай, когда я описал диск. google.com/file/d/0B-mn7bZcNqJMVkt0OXVLVVdnNTA/ - person F.M; 07.09.2015
comment
Да, я определенно не вижу красного цвета при переключении классов размера в конструкторе интерфейсов. Я использовал только любой размер. - person Ben Guild; 07.09.2015

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

for(UIView *currentView in self.arrangedSubviews){
    for(NSLayoutConstraint *currentConstraint in currentView.constraints){
        [currentConstraint setPriority:999];
    }
}
person sp00ky    schedule 14.03.2016
comment
У меня это не сработало, потому что механизм автоматической компоновки жалуется, когда вы меняете обязательное ограничение (priority = 1000) на необязательное (priority <= 999). - person Senseful; 22.06.2016

У меня был ряд кнопок с ограничением по высоте. Это происходит, когда одна кнопка скрыта. Установка приоритета ограничения высоты кнопок на 999 устранила проблему.

person Niklas    schedule 07.10.2016

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

person William Kinaan    schedule 14.09.2015

NOP с [mySubView removeFromSuperview]. Надеюсь, это могло кому-то помочь :)

person Grégoire GUYON    schedule 09.08.2016
comment
Извините, а что NOP? - person Pang; 10.08.2017