Jak pułapkować na UIViewAlertForUnsatisfiableConstraints?

234

W dzienniku debugowania pojawia się błąd:

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.

Jak przechwycić to połączenie? Nie pojawia się nigdzie w moim kodzie.

Zrzut ekranu 1

Maury Markowitz
źródło
W 9 na 10 przypadków: jest to po prostu spowodowane przez: dla niektórych widoków lub elementów na storyboardu yoru odznaczasz opcję „Zainstalowano”. (Na przykład po prostu przycisk programowania lub coś, czego już nie potrzebujesz). Ogólnie źle obsługuje „niezainstalowany”: często pozostawia ograniczenia, które stają się bez znaczenia bez niezainstalowanego elementu. Często rozwiązaniem jest po prostu usunięcie zapomnianych elementów, które siedzą wokół „niezainstalowanych” - wystarczy je usunąć.
Fattie

Odpowiedzi:

442

Ten post pomógł mi naprawdę dużo !

Dodałem UIViewAlertForUnsatisfiableConstraints symboliczne przerwania z proponowanych działań:

Projekt Obj-C

po [[UIWindow keyWindow] _autolayoutTrace]

Symboliczny punkt przerwania z niestandardową akcją w projekcie Cel-C

Szybki projekt

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

Symboliczny punkt przerwania z niestandardową akcją

Dzięki tej podpowiedzi dziennik stał się bardziej szczegółowy i łatwiej było mi określić, który widok złamał ograniczenie.

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

Potem wstrzymałem wykonywanie Pauza i zmieniłem kolor tła problematycznego widoku za pomocą polecenia ( oczywiście zastępując 0x7f88a8cc2050adres pamięci twojego obiektu ) ...

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

... i wynik To było niesamowite!

Widok podpowiedzi

Po prostu wspaniałe! Mam nadzieję, że to pomoże.

Thomás Calmon
źródło
3
@iAnurag Możesz uruchamiać polecenia w obszarze konsoli, gdy wykonywanie jest wstrzymane.
Thomás Calmon
2
@TomCalmon Zrobiłem to samo ... ale pokazuje następujący błąd 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]wraca nildo mnie
Igor Andreev
2
Upewnij się, że zastąpiłeś 0x7f88a8cc2050 adresem pamięci Twojego obiektu i uruchom polecenie w konsoli, gdy wykonywanie zostanie wstrzymane.
Tom Howard,
3
Niewiarygodne, niewiarygodne. Świetna wskazówka tutaj, całkowicie pomogła mi od razu przejść do problemu. Po zmianie przedmiotu na czerwony kontynuuj wykonywanie, jeśli to możliwe, a zobaczysz wyróżnienie.
Aaron
255

Będziesz chciał dodać Symbolic Breakpoint. Apple zapewnia doskonały przewodnik, jak to zrobić.

  1. Otwórz Breakpoint Navigator cmd+7( cmd+8w Xcode 9)
  2. Kliknij Addprzycisk w lewym dolnym rogu
  3. Wybierz Add Symbolic Breakpoint...
  4. Tam, gdzie jest napisane, Symbolpo prostu wpiszUIViewAlertForUnsatisfiableConstraints

Możesz także traktować go jak każdy inny punkt przerwania, włączając go i wyłączając, dodając akcje lub rejestrując wiadomości.

Stephen Furlani
źródło
55
Po prostu nie rozumiem, jak mogę lepiej debugować problem dzięki tej podpowiedzi. Dodałem symboliczny punkt przerwania, ale wciąż nie zapewnia mi wystarczającej ilości informacji na temat problemu. Jedynym sposobem jest czytanie linii po linii i zrozumienie, co powoduje problem ... w przeciwnym razie usunięcie ograniczeń i ponowne dodanie ich razem z podglądem w widoku asisstent powinno najbardziej pomóc!
Alex Cio,
11
Może to pomóc uzyskać więcej informacji po zatrzymaniu się w punkcie przerwania: staxmanade.com/2015/06/debugging-ios-autolayout-issues
fabb
1
Po prostu dodając, że możesz teraz nadawać identyfikatory ograniczeniom bezpośrednio w IB, więc kiedy je debugujesz, zobaczysz tę nazwę.
Mark A. Donohoe
2
(kontynuacja na @MarqueIV) NSLayoutConstraintma identifierwłaściwość od iOS 7 - Xcode 7 i wyżej , które można ustawić zarówno z IB Storyboardów, jak i z kodu. Ustawiając identyfikator, można łatwiej odróżnić ograniczenia generowane przez system i generowane przez użytkownika w dzienniku debugowania, np. myConstraint.identifier = "centered image"(Źródło i przykłady: useyourloaf.com/blog/using-identifiers-to-debug-autolayout )
PDK
@AlexCio Jak to pomaga? Przynajmniej tak robi, to zatrzymuje się w momencie, w którym się dzieje. Daje ślad stosu, w którym można cofnąć się i znaleźć pochodzenie ...
Honey
10

Postępował zgodnie z radą Stephena i próbował debugować kod i whoa! zadziałało. Odpowiedź leży w samym komunikacie debugowania.

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

Linia powyżej mówi, że środowisko wykonawcze działało poprzez usunięcie tego ograniczenia. Być może nie potrzebujesz odstępów poziomych na swoim przycisku (MPKnockoutButton). Po usunięciu tego ograniczenia nie będzie narzekać w czasie wykonywania i uzyskasz pożądane zachowanie.

Grupa strategiczna
źródło
3
Kompilator? Masz na myśli środowisko uruchomieniowe? Kompilator nie usunął ograniczenia. Kompilator zostawił go dla środowiska wykonawczego, dlatego „odzyskuje się przez zerwanie ograniczenia” podczas działania .
drhr
Tak,
miałem na
2

Ilekroć próbuję usunąć ograniczenia, które musiał złamać system, moje ograniczenia nie są już wystarczające, aby spełnić IB (tzn. „Brakujące ograniczenia” pokazują się w IB, co oznacza, że ​​są niekompletne i nie będą używane). W rzeczywistości udało mi się obejść ten problem, ustawiając ograniczenie, które chce złamać, na niski priorytet, co (i jest to założenie) pozwala systemowi z łatwością złamać ograniczenie. Prawdopodobnie nie jest to najlepsze rozwiązanie, ale rozwiązało mój problem, a wynikające z niego ograniczenia działały idealnie.

Nick Molyneux
źródło
2
Zazwyczaj jest to sytuacja, w której chcesz użyć ograniczenia symbolu zastępczego, które jest usuwane w czasie wykonywania. Aby ustawić ograniczenie jako ograniczenie zastępcze, przejdź do inspektora ograniczeń i kliknij „Usuń podczas kompilacji”. Zwróć uwagę, w jaki sposób symbol wiązki dwuteowej w obszarze rysunku IB zmienia kolor z niebieskiego na szary, aby to wskazać.
spencery2
1
Miałem ten sam problem. kiedy usuwam złamane ograniczenie, mój projekt się zepsuł. Ustawiłem więc priorytet na średni.
Jeremy Piednoel,