Centrar la X de la subvista en el diseño automático arroja "no preparado para la restricción"

82

Tengo una subclase UIView personalizada que se inicializa a través de una punta.

En -awakeFromNib, estoy creando una subvista e intento centrarla en su supervista.

[self setInteralView: [[UIView alloc] init]];
[[self internalView] addConstraint: [NSLayoutConstraint constraintWithItem: [self internalView]
                                                                 attribute: NSLayoutAttributeCenterX
                                                                 relatedBy: NSLayoutRelationEqual
                                                                    toItem: self
                                                                 attribute: NSLayoutAttributeCenterX
                                                                multiplier: 1
                                                                  constant: 0]];

Esto se rompe y provoca el siguiente resultado:

2013-08-11 17:58:29.628 MyApp[32414:a0b] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0xc1dcc80 UIView:0xc132a40.centerX == MyView:0xc1315a0.centerX>
    When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
2013-08-11 17:58:29.630 MyApp[32414:a0b] View hierarchy unprepared for constraint.
    Constraint: <NSLayoutConstraint:0xc1dcc80 UIView:0xc132a40.centerX == MyView:0xc1315a0.centerX>
    Container hierarchy: 
<UIView: 0xc132a40; frame = (0 0; 0 0); clipsToBounds = YES; layer = <CALayer: 0xc132bc0>>
    View not found in container hierarchy: <MyView: 0xc1315a0; frame = (-128 -118; 576 804); layer = <CALayer: 0xc131710>>
    That view's superview: <UIView: 0xc131a70; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0xc131b50>>
Patrick Perini
fuente

Respuestas:

95

Como indica el error, debe agregar la restricción a una vista de modo que las vistas involucradas en la restricción sean la vista a la que está agregando o una subvista de esa vista. En el caso de su código anterior, debería agregar esa restricción a self, en lugar de [self internalView]. La razón por la que no funciona como está escrito es que una de las vistas involucradas en la restricción ( self) no está en la jerarquía de vistas si considera solo [self internalView]y más abajo).

Para obtener más detalles, consulte la sección de discusión de la documentación sobre el addConstraint:método.

Aquí está su línea de código, arreglada como sugiero:

UIView* internalView = [[UIView alloc] initWithFrame:CGRectZero];
internalView.translatesAutoresizingMaskIntoConstraints = NO;
[self setInternalView:internalView];

[self addConstraint: [NSLayoutConstraint constraintWithItem: [self internalMapView]
                                         attribute: NSLayoutAttributeCenterX
                                         relatedBy: NSLayoutRelationEqual
                                         toItem: self
                                         attribute: NSLayoutAttributeCenterX
                                         multiplier: 1
                                         constant: 0]];
Charles A.
fuente
Eso me da un error al reclamar Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.Y luego se rompe<NSLayoutConstraint:0x126ad4c0 UIView:0xadd2e20.centerX == MyView:0xadd19b0.centerX>
Patrick Perini
1
Probablemente porque no desactivó la traducción de la máscara de cambio de tamaño automático en restricciones. Actualizaré mi código anterior para hacerlo. Probablemente también necesitará agregar restricciones de ancho / alto para que sea visible, y recomendaría agregar una restricción en el centro Y o algún tipo de restricción superior / inferior para que el diseño esté bien definido.
Charles A.
entonces no puedes tener un diseño como @"V:|[v]|":? como esto se referiría a una supervista?
Sam
1
@Sam Esa es una restricción válida, pero no centrará la vista representada por [v], le dará restricciones superior e inferior del tamaño estándar entre [v]los bordes superior e inferior y el de su supervista. Creo que también tendrías que agregarlo a la supervista.
Charles A.
8
Una regla de pulgar simple: cualquier restricción que se relacione con el tamaño de una vista debe agregarse a la misma vista y cualquier restricción que se relacione con su posición debe agregarse a su padre.
Deepak GM
117

Para otros que vienen aquí: Recibí este error porque agregué las restricciones antes de agregar las subvistas a la jerarquía.

Jannie Theunissen
fuente
¡Gracias, me ahorraste mucho tiempo!
Stas
Esta es la razón más común de esta falla que he notado hasta ahora.
Ayan Sengupta
exactamente y cuando lo hagas, constraintWithItem: youViewel artículo no existe en él.
Nazir
36

Respuesta corta : intercambia la vista principal y secundaria.
Suponiendo que selfes la vista principal:

  • malo :[subview addConstraint [NSLayoutConstraint constraintWithItem:self...

  • bueno :[self addConstraint [NSLayoutConstraint constraintWithItem:subview...


Rápido

subview.translatesAutoresizingMaskIntoConstraints = false
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
    "H:|-0-[subview]-0-|",
    options: .DirectionLeadingToTrailing,
    metrics: nil,
    views: ["subview":subview]))

Obj-C

SwiftArquitecto
fuente
10

Consejos adicionales:

Tengo una aplicación desarrollada recientemente que funciona bien en iOS7 pero aparece el error en iOS8 "La jerarquía de vista no está preparada para la restricción".

Lea otra publicación que dice que las (sub) vistas deben agregarse antes de crear restricciones. Hice eso pero sigo recibiendo el error.

Luego descubrí que debería crear restricciones en - (void) viewDidAppear en lugar de viewDidLoad

Kevin
fuente
2
Cuando viewDidAppear()se llama, la vista ya es visible para el usuario. Si modifica restricciones, debería ser antes de eso, en viewWillLayoutSubviews(). Más del ciclo de vida aquí: medium.com/@SergiGracia/…
maganap
6

Esta es una pregunta antigua, pero quiero señalar esta línea de los documentos:

Al desarrollar para iOS 8.0 o posterior, establezca la propiedad isActive de la restricción en true en lugar de llamar al método addConstraint (_ :) directamente. La propiedad isActive agrega y elimina automáticamente la restricción de la vista correcta.

Como mínimo, esto eliminará un punto de falla, ya que ya no tendrá que preocuparse por llamar a addConstraint en la vista incorrecta.

WongWray
fuente
2

En mi caso, se resolvió utilizando la vista raíz como un contenedor de restricciones en lugar de una vista creada actualmente.

Quiero decir, use inside viewController

view.addConstraints([leftConstraintImage, topConstraintImage]) en vez de imageView.addConstraints...

Vyacheslav
fuente
1

Como @CharlesA señala en su respuesta, el problema es que la vista que está agregando no está en la jerarquía de vista adecuada. Su respuesta es buena, pero voy a dar algunos detalles para un caso de uso particular que me causó este problema:

Esto sucedió cuando intenté agregar restricciones a una vista cuyo controlador de vista había creado una instancia usando instantiateViewControllerWithIdentifier. Para resolver el problema, utilicé [childViewController didMoveToParentViewController:self]. Esto agregó la vista a la jerarquía de vistas a la que hacía referencia mi restricción.

benvolioT
fuente
0

Primero agregue su subView y luego agregue sus restricciones como esta

addSubview(yourSubViewHere)
yourSubViewHere.translatesAutoresizingMaskIntoConstraints = false

addConstraint(NSLayoutConstraint(....
oskarko
fuente