Как перехватить UIViewAlertForUnsatisfiableConstraints?

234

Я вижу ошибку в моем журнале отладчика:

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x191f0920 H:[MPKnockoutButton:0x17a876b0]-(34)-[MPDetailSlider:0x17a8bc50](LTR)>

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.

Как мне перехватить этот звонок? Это не появляется нигде в моем коде.

Screenshot1

Мори Марковиц
источник
В 9 из 10 случаев: это просто вызвано тем, что: для какого-либо представления или элемента на раскадровке вы снимите флажок «Установлено». (Например, просто кнопка разработки или что-то, что вам больше не нужно.) В общем, он плохо обрабатывает «not-Installed»: он часто оставляет там ограничения, которые становятся бессмысленными без элемента Not-Installed. Часто решение состоит в том, чтобы просто удалить элементы, о которых вы забыли, которые сидят вокруг «не установлено» - просто удалите их.
Толстяк

Ответы:

442

Это сообщение помогло мне МНОГО !

Я добавил символическую точку останова UIViewAlertForUnsatisfiableConstraints с предлагаемым действием:

Проект Obj-C

po [[UIWindow keyWindow] _autolayoutTrace]

Символическая точка останова с настраиваемым действием в проекте Objective-C

Свифт проект

expr -l objc++ -O -- [[UIWindow keyWindow] _autolayoutTrace]

Символическая точка останова с настраиваемым действием

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

UIWindow:0x7f88a8e4a4a0
|   UILayoutContainerView:0x7f88a8f23b70
|   |   UINavigationTransitionView:0x7f88a8ca1970
|   |   |   UIViewControllerWrapperView:0x7f88a8f2aab0
|   |   |   |   UIView:0x7f88a8ca2880
|   |   |   |   |   *UIView:0x7f88a8ca2a10
|   |   |   |   |   |   *UIButton:0x7f88a8c98820'Archived'
|   |   |   |   |   |   |   UIButtonLabel:0x7f88a8cb0e30'Archived'
|   |   |   |   |   |   *UIButton:0x7f88a8ca22d0'Download'
|   |   |   |   |   |   |   UIButtonLabel:0x7f88a8cb04e0'Download'
|   |   |   |   |   |   *UIButton:0x7f88a8ca1580'Deleted'
|   |   |   |   |   |   |   UIButtonLabel:0x7f88a8caf100'Deleted'
|   |   |   |   |   *UIView:0x7f88a8ca33e0
|   |   |   |   |   *_UILayoutGuide:0x7f88a8ca35b0
|   |   |   |   |   *_UILayoutGuide:0x7f88a8ca4090
|   |   |   |   |   _UIPageViewControllerContentView:0x7f88a8f1a390
|   |   |   |   |   |   _UIQueuingScrollView:0x7f88aa031c00
|   |   |   |   |   |   |   UIView:0x7f88a8f38070
|   |   |   |   |   |   |   UIView:0x7f88a8f381e0
|   |   |   |   |   |   |   |   UIView:0x7f88a8f39fa0, MISSING HOST CONSTRAINTS
|   |   |   |   |   |   |   |   |   *UIButton:0x7f88a8cb9bf0'Retrieve data'- AMBIGUOUS LAYOUT for UIButton:0x7f88a8cb9bf0'Retrieve data'.minX{id: 170}, UIButton:0x7f88a8cb9bf0'Retrieve data'.minY{id: 171}
|   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8f3ad80- AMBIGUOUS LAYOUT for UIImageView:0x7f88a8f3ad80.minX{id: 172}, UIImageView:0x7f88a8f3ad80.minY{id: 173}
|   |   |   |   |   |   |   |   |   *App.RecordInfoView:0x7f88a8cbe530- AMBIGUOUS LAYOUT for App.RecordInfoView:0x7f88a8cbe530.minX{id: 174}, App.RecordInfoView:0x7f88a8cbe530.minY{id: 175}, App.RecordInfoView:0x7f88a8cbe530.Width{id: 176}, App.RecordInfoView:0x7f88a8cbe530.Height{id: 177}
|   |   |   |   |   |   |   |   |   |   +UIView:0x7f88a8cc1d30- AMBIGUOUS LAYOUT for UIView:0x7f88a8cc1d30.minX{id: 178}, UIView:0x7f88a8cc1d30.minY{id: 179}, UIView:0x7f88a8cc1d30.Width{id: 180}, UIView:0x7f88a8cc1d30.Height{id: 181}
|   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8cc1ec0- AMBIGUOUS LAYOUT for UIView:0x7f88a8cc1ec0.minX{id: 153}, UIView:0x7f88a8cc1ec0.minY{id: 151}, UIView:0x7f88a8cc1ec0.Width{id: 154}, UIView:0x7f88a8cc1ec0.Height{id: 165}
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8e68e10- AMBIGUOUS LAYOUT for UIView:0x7f88a8e68e10.minX{id: 155}, UIView:0x7f88a8e68e10.minY{id: 150}, UIView:0x7f88a8e68e10.Width{id: 156}
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e65de0- AMBIGUOUS LAYOUT for UIImageView:0x7f88a8e65de0.minX{id: 159}, UIImageView:0x7f88a8e65de0.minY{id: 182}
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8e69080'8-6-2015'- AMBIGUOUS LAYOUT for UILabel:0x7f88a8e69080'8-6-2015'.minX{id: 183}, UILabel:0x7f88a8e69080'8-6-2015'.minY{id: 184}, UILabel:0x7f88a8e69080'8-6-2015'.Width{id: 185}
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cc0690'16:34'- AMBIGUOUS LAYOUT for UILabel:0x7f88a8cc0690'16:34'.minX{id: 186}, UILabel:0x7f88a8cc0690'16:34'.minY{id: 187}, UILabel:0x7f88a8cc0690'16:34'.Width{id: 188}, UILabel:0x7f88a8cc0690'16:34'.Height{id: 189}
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8cc2050- AMBIGUOUS LAYOUT for UIView:0x7f88a8cc2050.minX{id: 161}, UIView:0x7f88a8cc2050.minY{id: 166}, UIView:0x7f88a8cc2050.Width{id: 163}
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e69d90- AMBIGUOUS LAYOUT for UIImageView:0x7f88a8e69d90.minX{id: 190}, UIImageView:0x7f88a8e69d90.minY{id: 191}, UIImageView:0x7f88a8e69d90.Width{id: 192}, UIImageView:0x7f88a8e69d90.Height{id: 193}
|   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3cc00
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8e618d0
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e5ba10
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3cd70
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e58e10
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e5e7a0
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3cee0
|   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3dc70
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8e64dd0
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8e65290'Average flow rate'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8e712d0'177.0 ml/s'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8c97150'1299.4'
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3dde0
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8f3df50'Maximum flow rate'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cbfdb0'371.6 ml/s'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cc0230'873.5'
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3e2a0
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8f3e410'Total volume'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cc0f20'371.6 ml'
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3e870
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8f3ea00'Time do max. flow'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cc0ac0'3.6 s'
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3ee10
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8f3efa0'Flow time'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cbf980'2.1 s'
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3f3e0
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8f3f570'Voiding time'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cc17e0'3.5 s'
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3f9a0
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8f3fb30'Voiding delay'
|   |   |   |   |   |   |   |   |   |   |   |   *UILabel:0x7f88a8cc1380'1.0 s'
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8e65000
|   |   |   |   |   |   |   |   |   |   |   |   *UIButton:0x7f88a8e52f20'Show'
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e6e1d0
|   |   |   |   |   |   |   |   |   |   |   |   *UIButton:0x7f88a8e52c90'Send'
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e61bb0
|   |   |   |   |   |   |   |   |   |   |   |   *UIButton:0x7f88a8e528e0'Delete'
|   |   |   |   |   |   |   |   |   |   |   |   *UIImageView:0x7f88a8e6b3f0
|   |   |   |   |   |   |   |   |   |   |   |   *UIView:0x7f88a8f3ff60
|   |   |   |   |   |   |   |   |   *UIActivityIndicatorView:0x7f88a8cba080
|   |   |   |   |   |   |   |   |   |   UIImageView:0x7f88a8cba700
|   |   |   |   |   |   |   |   |   *_UILayoutGuide:0x7f88a8cc3150
|   |   |   |   |   |   |   |   |   *_UILayoutGuide:0x7f88a8cc3b10
|   |   |   |   |   |   |   UIView:0x7f88a8f339c0
|   |   UINavigationBar:0x7f88a8c96810
|   |   |   _UINavigationBarBackground:0x7f88a8e45c00
|   |   |   |   UIImageView:0x7f88a8e46410
|   |   |   UINavigationItemView:0x7f88a8c97520'App'
|   |   |   |   UILabel:0x7f88a8c97cc0'App'
|   |   |   UINavigationButton:0x7f88a8e3e850
|   |   |   |   UIImageView:0x7f88a8e445b0
|   |   |   _UINavigationBarBackIndicatorView:0x7f88a8f2b530

Legend:
    * - is laid out with auto layout
    + - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES
     - layout engine host

Затем я приостановил выполнение Пауза и изменил цвет фона проблемного вида с помощью команды ( конечно, заменив 0x7f88a8cc2050ее адресом памяти вашего объекта ) ...

Obj-C,

expr ((UIView *)0x7f88a8cc2050).backgroundColor = [UIColor redColor]

Swift 3.0

expr -l Swift -- import UIKit
expr -l Swift -- unsafeBitCast(0x7f88a8cc2050, to: UIView.self).backgroundColor = UIColor.red

... и результат был потрясающим!

Подсказка

Просто удивительно! Надеюсь, поможет.

Томас Калмон
источник
3
@iAnurag Вы можете запускать команды в области консоли, когда выполнение приостановлено.
Томас Калмон
2
@ TomCalmon Я сделал то же самое ... но он показывает следующую ошибку rror: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x7f88a8cc2050). The process has been returned to the state before expression evaluation.
iAnurag
2
expr -l objc++ -O -- [[UIWindow keyWindow] _autolayoutTrace]возвращается nilза мной
Игорь Андреев
2
Убедитесь, что вы заменили 0x7f88a8cc2050 адресом памяти вашего объекта и запускаете команду в консоли, когда выполнение приостанавливается.
Том Ховард
3
Unfreakingbelievable. Отличный совет, полностью помог мне перейти прямо к вопросу. Как только элемент поменяется на красный, продолжите выполнение, если это возможно, и вы увидите выделение.
Аарон
255

Вы хотите добавить Symbolic Breakpoint. Apple предоставляет отличное руководство о том, как это сделать.

  1. Откройте Навигатор Точки останова cmd+7( cmd+8в Xcode 9)
  2. Нажмите Addкнопку в левом нижнем углу
  3. Выбрать Add Symbolic Breakpoint...
  4. Где написано, Symbolпросто введитеUIViewAlertForUnsatisfiableConstraints

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

Стивен Фурлани
источник
55
Я просто не понимаю, как лучше отладить проблему с помощью этой подсказки. Я добавил символическую точку останова, но она все еще не дает мне достаточно информации, в чем проблема. Единственный способ - попытаться читать построчно и понять, в чем причина проблемы .... в противном случае очистка ограничений и повторное добавление их вместе с предварительным просмотром в произвольном представлении должно помочь большинству!
Алекс Чио
11
Это может помочь получить больше информации после остановки в точке останова: staxmanade.com/2015/06/debugging-ios-autolayout-issues
fabb
1
Просто добавьте, что теперь вы можете присваивать идентификаторы ограничениям непосредственно в IB, поэтому при их отладке вы увидите это имя.
Марк А. Донохо,
2
(продолжение @MarqueIV) NSLayoutConstraintимеет identifierсвойство начиная с iOS 7 - Xcode 7 и выше , которое может быть установлено как из IB Storyboards, так и из кода. Установив идентификатор, вы можете легче различать системные и пользовательские ограничения в журнале отладки, например myConstraint.identifier = "centered image"(источник и примеры: useyourloaf.com/blog/using-identifiers-to-debug-autolayout )
PDK
@AlexCio Как это помогает? По крайней мере, он делает паузу в тот момент, когда это происходит. Он дает трассировку стека, где вы можете вернуться и найти происхождение ...
Мед
10

Последовал совету Стивена и попытался отладить код и все! это сработало. Ответ лежит в самом отладочном сообщении.

Will attempt to recover by breaking constraint NSLayoutConstraint:0x191f0920 H:[MPKnockoutButton:0x17a876b0]-(34)-[MPDetailSlider:0x17a8bc50](LTR)>

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

Sategroup
источник
3
Компилятор? Вы имеете в виду время выполнения? Компилятор не снял ограничение. Компилятор оставил его там для выполнения во время выполнения, поэтому «восстанавливается путем нарушения ограничений» во время выполнения .
Drhr
Да, я имел в виду время выполнения
Sategroup
2

Всякий раз, когда я пытаюсь удалить ограничения, которые система должна была нарушить, моих ограничений уже недостаточно для удовлетворения IB (то есть «отсутствующие ограничения» отображаются в IB, что означает, что они неполные и не будут использоваться). Я на самом деле обошел это, установив ограничение, которое оно хочет нарушить, на низкий приоритет, что (и это предположение) позволяет системе грациозно нарушить ограничение. Возможно, это не лучшее решение, но оно решило мою проблему, и полученные ограничения сработали отлично.

Ник Молинье
источник
2
Как правило, это ситуация, когда вы хотите использовать ограничение заполнителя, которое удаляется во время выполнения. Чтобы сделать ограничение заполнителем, перейдите в инспектор ограничений и нажмите «Удалить во время сборки». Обратите внимание, как символ I-луча ограничения в области рисования IB меняется с синего на серый, чтобы показать это.
spencery2
1
Я была такая же проблема. когда я удаляю сломанное ограничение, мой дизайн сломался. Поэтому я установил приоритет на средний.
Джереми Пьедноэль