Comment intercepter sur UIViewAlertForUnsatisfiableConstraints?

234

Je vois une erreur apparaître dans mon journal de débogage:

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.

Comment puis-je intercepter cet appel? Il n'apparaît nulle part dans mon code.

Screenshot1

Maury Markowitz
la source
Dans 9 cas sur 10: cela est simplement dû à: pour une vue ou un élément sur le storyboard yoru, vous décochez "Installé". (Par exemple, juste un bouton de développement ou quelque chose dont vous n'avez plus besoin.) En général, il gère mal "non installé": il laisse souvent là des contraintes qui perdent leur sens sans l'élément non installé. Souvent, la solution consiste simplement à supprimer les éléments que vous avez oubliés, qui se trouvent «non installés» - il suffit de les supprimer.
Fattie

Réponses:

442

Ce message m'a beaucoup aidé !

J'ai ajouté le point d'arrêt symbolique UIViewAlertForUnsatisfiableConstraints avec l'action suggérée:

Projet Obj-C

po [[UIWindow keyWindow] _autolayoutTrace]

Point d'arrêt symbolique avec action personnalisée dans le projet Objective-C

Projet Swift

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

Point d'arrêt symbolique avec action personnalisée

Avec cet indice, le journal est devenu plus détaillé, et il m'a été plus facile d'identifier quelle vue avait la contrainte brisée.

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

Ensuite, j'ai suspendu l'exécution Pause et j'ai changé la couleur d'arrière-plan de la vue problématique avec la commande (en la remplaçant 0x7f88a8cc2050par l'adresse mémoire de votre objet bien sûr) ...

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

... et le résultat C'était génial!

Vue suggérée

Simplement extraordinaire! J'espère que ça aide.

Thomás Calmon
la source
3
@iAnurag Vous pouvez exécuter des commandes dans la zone de la console, lorsque l'exécution est suspendue.
Thomás Calmon
2
@TomCalmon J'ai fait la même chose ... mais cela montre l'erreur suivante 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]revient nilpour moi
Igor Andreev
2
Assurez-vous de remplacer 0x7f88a8cc2050 par l'adresse mémoire de votre objet et exécutez la commande dans la console lorsque l'exécution s'arrête.
Tom Howard
3
Unfreakingbelievable. Un bon conseil ici, m'a totalement aidé à aller directement au problème. Une fois que l'élément est passé au rouge, continuez l'exécution si possible et vous verrez le point culminant.
Aaron
255

Vous voudrez ajouter un Symbolic Breakpoint. Apple fournit un excellent guide sur la façon de procéder.

  1. Ouvrez le Breakpoint Navigator cmd+7( cmd+8dans Xcode 9)
  2. Cliquez sur le Addbouton en bas à gauche
  3. Sélectionner Add Symbolic Breakpoint...
  4. Où il est écrit, Symboltapez simplementUIViewAlertForUnsatisfiableConstraints

Vous pouvez également le traiter comme tout autre point d'arrêt, l'activer et le désactiver, ajouter des actions ou consigner des messages.

Stephen Furlani
la source
55
Je ne comprends tout simplement pas comment je peux mieux déboguer le problème avec cet indice. J'ai ajouté un point d'arrêt symbolique mais cela ne me donne toujours pas assez d'informations sur le problème. La seule façon est d'essayer de lire ligne par ligne et de comprendre ce qui cause le problème ... sinon, effacer les contraintes et les ajouter à nouveau avec l'aperçu dans la vue asisstent devrait aider le plus!
Alex Cio
11
Cela pourrait aider à obtenir plus d'informations après un arrêt au point d'arrêt: staxmanade.com/2015/06/debugging-ios-autolayout-issues
fabb
1
Il suffit d'ajouter que vous pouvez désormais attribuer des identifiants aux contraintes directement dans IB, donc lorsque vous les déboguez, c'est le nom que vous verrez.
Mark A. Donohoe
2
(suivi de @MarqueIV) NSLayoutConstrainta une identifierpropriété depuis iOS 7 - Xcode 7 et supérieur , qui peut être définie à la fois à partir des storyboards IB ainsi qu'à partir du code. En définissant l'identifiant, vous pouvez plus facilement distinguer les contraintes générées par le système et celles générées par l'utilisateur dans le journal de débogage, par exemple myConstraint.identifier = "centered image"(source et exemples: useyourloaf.com/blog/using-identifiers-to-debug-autolayout )
PDK
@AlexCio Comment ça aide? Le moins qu'il fasse, c'est qu'il s'arrête au moment où cela se produit. Il donne une trace de pile où vous pouvez revenir en arrière et trouver l'origine ...
Honey
10

J'ai suivi les conseils de Stephen et essayé de déboguer le code et whoa! ça a marché. La réponse réside dans le message de débogage lui-même.

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

La ligne ci-dessus vous indique que le runtime a fonctionné en supprimant cette contrainte. Il se peut que vous n'ayez pas besoin d'espacement horizontal sur votre bouton (MPKnockoutButton). Une fois que vous avez supprimé cette contrainte, elle ne se plaindra pas lors de l'exécution et vous obtiendrez le comportement souhaité.

Sategroup
la source
3
Le compilateur? Vous voulez dire le temps d'exécution? Le compilateur n'a pas supprimé la contrainte. Le compilateur l'a laissé là pour que le runtime soit traité, d'où "récupérer en cassant la contrainte" pendant le runtime .
drhr
Ouais, je voulais dire runtime
Sategroup
2

Chaque fois que j'essaie de supprimer les contraintes que le système a dû casser, mes contraintes ne sont plus suffisantes pour satisfaire l'IB (c.-à-d. "Les contraintes manquantes" s'affichent dans l'IB, ce qui signifie qu'elles sont incomplètes et ne seront pas utilisées). J'ai réussi à contourner ce problème en définissant la contrainte qu'il veut rompre à une priorité faible, ce qui (et c'est une hypothèse) permet au système de rompre la contrainte avec élégance. Ce n'est probablement pas la meilleure solution, mais cela a résolu mon problème et les contraintes résultantes ont parfaitement fonctionné.

Nick Molyneux
la source
2
Il s'agit généralement d'une situation dans laquelle vous souhaitez utiliser une contrainte d'espace réservé qui est supprimée lors de l'exécution. Pour faire d'une contrainte une contrainte d'espace réservé, accédez à l'inspecteur de contraintes et cliquez sur "Supprimer au moment de la création". Remarquez comment le symbole de poutre en I de contrainte dans la zone de dessin IB passe du bleu au gris pour l'indiquer.
spencery2
1
J'ai eu le même problème. lorsque je supprime la contrainte brisée, ma conception s'est cassée. J'ai donc défini la priorité sur moyen.
Jeremy Piednoel