Como interceptar o UIViewAlertForUnsatisfiableConstraints?

234

Estou vendo um erro aparecer no meu log do depurador:

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.

Como faço para interceptar essa ligação? Ele não aparece em nenhum lugar do meu código.

Captura de tela1

Maury Markowitz
fonte
Em 9 de 10 casos: isso é causado apenas por: para alguma exibição ou item no yoru storyboard, você desmarca "Instalado". (Por exemplo, apenas um botão de desenvolvimento ou algo que você não precisava mais.) Em geral, ele lida com "não instalado": muitas vezes deixa restrições ali que se tornam sem sentido sem o item não instalado. Freqüentemente, a solução é simplesmente excluir itens que você esqueceu, que estão parados "não instalados" - apenas exclua-os.
Fattie

Respostas:

442

Este post me ajudou MUITO !

Eu adicionei UIViewAlertForUnsatisfiableConstraints ponto de interrupção simbólica com ação sugerida:

Projeto Obj-C

po [[UIWindow keyWindow] _autolayoutTrace]

Ponto de interrupção simbólico com ação personalizada no projeto Objective-C

Projeto Swift

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

Ponto de interrupção simbólico com ação personalizada

Com essa dica, o log ficou mais detalhado e foi mais fácil para mim identificar qual visualização tinha a restrição quebrada.

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

Em seguida, parei a execução Pausa e alterei a cor de fundo da visualização problemática com o comando (substituindo 0x7f88a8cc2050pelo endereço de memória do seu objeto, é claro) ...

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

... e o resultado Foi incrível!

Vista sugerida

Simplesmente incrível! Espero que ajude.

Thomás Calmon
fonte
3
@iAnurag Você pode executar comandos na área do console, quando a execução estiver em pausa.
Thomás Calmon
2
@TomCalmon Fiz o mesmo ... mas mostra o seguinte erro 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]retorna nilpara mim
Igor Andreev 06/04
2
Substitua 0x7f88a8cc2050 pelo endereço de memória do seu objeto e execute o comando no console quando a execução for pausada.
Tom Howard
3
Inacreditável. Ótima dica aqui, totalmente me ajudou a ir direto para a questão. Depois que o item for alterado para vermelho, continue a execução, se possível, e você verá o destaque.
Aaron
255

Você deseja adicionar um Symbolic Breakpoint. A Apple fornece um excelente guia sobre como fazer isso.

  1. Abra o Navegador do ponto de interrupção cmd+7( cmd+8no Xcode 9)
  2. Clique no Addbotão no canto inferior esquerdo
  3. Selecione Add Symbolic Breakpoint...
  4. Onde diz Symbolapenas digitarUIViewAlertForUnsatisfiableConstraints

Você também pode tratá-lo como qualquer outro ponto de interrupção, ativando e desativando, adicionando ações ou registrando mensagens.

Stephen Furlani
fonte
55
Só não entendo como posso depurar melhor o problema com essa dica. Eu adicionei um ponto de interrupção simbólico, mas ele ainda não me fornece informações suficientes, qual é o problema. A única maneira é tentar ler linha por linha e entender o que causa o problema ... caso contrário, limpar as restrições e adicioná-las novamente com a Visualização na visualização asisstent deve ajudar mais!
Alex Cio
11
Isso pode ajudar a obter mais informações após a parada no ponto de interrupção: staxmanade.com/2015/06/debugging-ios-autolayout-issues
fabb
1
Apenas acrescentando que agora você pode fornecer identificadores para as restrições diretamente no IB; portanto, quando você estiver depurando, esse é o nome que você verá.
MarkDonohoe #
2
(acompanhamento de @MarqueIV) NSLayoutConstraintpossui uma identifierpropriedade desde o iOS 7 - Xcode 7 e superior , que pode ser configurada no IB Storyboards e no código. Ao definir o identificador, você pode distinguir com mais facilidade entre restrições geradas pelo sistema e geradas pelo usuário no log de depuração, por exemplo myConstraint.identifier = "centered image"(origem e exemplos: useyourloaf.com/blog/using-identifiers-to-debug-autolayout )
PDK
@AlexCio Como isso ajuda? O mínimo que faz é parar no momento em que acontece. Ele fornece um rastreamento de pilha, no qual você pode voltar atrás e encontrar a origem ... #
186
10

Seguiu o conselho de Stephen e tentou depurar o código e whoa! funcionou. A resposta está na própria mensagem de depuração.

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

A linha acima informa que o tempo de execução funcionou removendo essa restrição. Talvez você não precise do Espaçamento Horizontal no botão (MPKnockoutButton). Depois de limpar essa restrição, ela não se queixará no tempo de execução e você obterá o comportamento desejado.

Sategroup
fonte
3
O compilador? Você quer dizer o tempo de execução? O compilador não removeu a restrição. O compilador deixou lá para o tempo de execução, portanto, "recupere quebrando restrições" durante o tempo de execução .
drhr 30/07/2015
Sim, eu quis dizer tempo de execução
Sategroup
2

Sempre que tento remover as restrições que o sistema precisou quebrar, minhas restrições não são mais suficientes para satisfazer o IB (ou seja, "restrições ausentes" aparecem no IB, o que significa que elas estão incompletas e não serão usadas). Na verdade, eu resolvi isso definindo a restrição que ele deseja quebrar com baixa prioridade, o que (e isso é uma suposição) permite que o sistema rompa a restrição normalmente. Provavelmente não é a melhor solução, mas resolveu meu problema e as restrições resultantes funcionaram perfeitamente.

Nick Molyneux
fonte
2
Normalmente, essa é uma situação em que você deseja usar uma restrição de espaço reservado que é removida no tempo de execução. Para tornar uma restrição uma restrição de espaço reservado, vá para o inspetor de restrições e clique em "Remover no momento da criação". Observe como o símbolo do feixe I de restrição na área de desenho IB muda de azul para cinza para indicar isso.
Spencery2
1
Eu tive o mesmo problema. Quando removo a restrição quebrada, meu design quebrou. Então, defino a prioridade como média.
Jeremy Piednoel 26/09/16