Wyśrodkowanie X widoku podrzędnego w autoukładzie powoduje wyświetlenie „nie przygotowane na ograniczenie”

82

Mam niestandardową podklasę UIView, która jest inicjowana za pomocą stalówki.

W programie -awakeFromNibtworzę podwidok i próbuję go wyśrodkować w jego superwidoku.

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

To przerywa i powoduje następujące dane wyjściowe:

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
źródło

Odpowiedzi:

95

Zgodnie z informacją o błędzie należy dodać ograniczenie do widoku w taki sposób, aby widoki zaangażowane w ograniczenie były widokiem, do którego je dodajesz, lub podziałem tego widoku. W przypadku powyższego kodu powinieneś dodać to ograniczenie do self, a nie [self internalView]. Przyczyną, dla której nie działa tak, jak napisano, jest jeden z widoków zaangażowanych w ograniczenie ( self), którego nie ma w hierarchii widoków, jeśli weźmiesz pod uwagę tylko [self internalView]i poniżej).

Aby uzyskać więcej informacji, zobacz sekcję poświęconą dyskusji w dokumentacji dotyczącej addConstraint:metody.

Oto twoja linia kodu, poprawiona tak, jak sugeruję:

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.
źródło
To daje mi błąd, twierdząc, Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.a potem się psuje<NSLayoutConstraint:0x126ad4c0 UIView:0xadd2e20.centerX == MyView:0xadd19b0.centerX>
Patrick Perini,
1
Najprawdopodobniej dlatego, że nie wyłączyłeś tłumaczenia automatycznej zmiany rozmiaru maski na ograniczenia. Aby to zrobić, zaktualizuję mój kod powyżej. Prawdopodobnie będziesz musiał również dodać ograniczenia szerokości / wysokości, aby był widoczny, i zalecałbym dodanie ograniczenia na środku Y lub jakiegoś rodzaju ograniczenia górnego / dolnego do siebie, aby dobrze zdefiniować układ.
Charles A.,
więc nie możesz mieć układu takiego jak @"V:|[v]|":? ponieważ odnosi się to do superwizji?
Sam
1
@Sam To poprawne ograniczenie, ale nie wyśrodkuje widoku reprezentowanego przez [v], da ci górne i dolne ograniczenia o standardowym rozmiarze między [v]górną i dolną krawędzią a widokiem nadzoru. Uważam, że musiałbyś również dodać to do superwizji.
Charles A.
8
Prosta reguła kciuka: wszelkie ograniczenia związane z rozmiarem widoku należy dodać do tego samego widoku, a wszelkie ograniczenia związane z jego położeniem należy dodać do jego elementu nadrzędnego.
Deepak GM
117

Dla innych, którzy tu przyjeżdżają: otrzymałem ten błąd, ponieważ dodałem ograniczenia przed dodaniem podglądów podrzędnych do hierarchii.

Jannie Theunissen
źródło
Dzięki, zaoszczędziłeś mi dużo czasu!
Staś
To jest najczęstsza przyczyna tego niepowodzenia, jaką zauważyłem do tej pory.
Ayan Sengupta
dokładnie i kiedy to zrobisz, constraintWithItem: youViewprzedmiot w nim nie istnieje!
Nazir
36

Krótka odpowiedź : zamień widok rodzica i dziecka.
Zakładając, że selfjest to widok nadrzędny:

  • źle :[subview addConstraint [NSLayoutConstraint constraintWithItem:self...

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


Szybki

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

Obj-C

SwiftArchitect
źródło
10

Dodatkowe wskazówki:

Mam nowo opracowaną aplikację, która działa dobrze na iOS7, ale pojawia się błąd na iOS8 „Hierarchia widoku nie jest przygotowana na ograniczenie”.

Przeczytaj inny post mówiący, że (pod) widoki muszą zostać dodane przed utworzeniem ograniczeń. Zrobiłem to, ale nadal mam błąd.

Potem doszedłem do wniosku, że powinienem utworzyć ograniczenia pod - (void) viewDidAppear zamiast viewDidLoad

Kevin
źródło
2
Po viewDidAppear()wywołaniu widok jest już widoczny dla użytkownika. Jeśli modyfikujesz ograniczenia, powinno to być wcześniej, w viewWillLayoutSubviews(). Więcej o cyklu życia tutaj: medium.com/@SergiGracia/ ...
maganap
6

To jest stare pytanie, ale chcę zwrócić uwagę na tę linię z dokumentów:

Podczas tworzenia aplikacji dla systemu iOS 8.0 lub nowszego należy ustawić właściwość isActive ograniczenia na wartość true zamiast bezpośrednio wywoływać metodę addConstraint (_ :). Właściwość isActive automatycznie dodaje i usuwa wiązanie z prawidłowego widoku.

Przynajmniej usunie to jeden punkt awarii, ponieważ nie musisz już martwić się o wywołanie addConstraint w złym widoku

WongWray
źródło
2

W moim przypadku zostało to rozwiązane poprzez użycie widoku głównego jako kontenera ograniczeń zamiast aktualnie utworzonego widoku.

Mam na myśli, użyj inside viewController

view.addConstraints([leftConstraintImage, topConstraintImage]) zamiast imageView.addConstraints...

Wiaczesław
źródło
1

Jak @CharlesA wskazuje w swojej odpowiedzi, problem polega na tym, że dodawany widok nie znajduje się we właściwej hierarchii widoków. Jego odpowiedź jest dobra, ale zamierzam dorzucić trochę szczegółów dla konkretnego przypadku użycia, który spowodował ten problem:

Zdarzyło mi się to, gdy próbowałem dodać ograniczenia do widoku, którego kontroler widoku utworzyłem przy użyciu instantiateViewControllerWithIdentifier. Aby rozwiązać problem, użyłem [childViewController didMoveToParentViewController:self]. To dodało widok do hierarchii widoków, do której odwoływało się moje ograniczenie.

benvolioT
źródło
0

Najpierw dodaj swój subView, a następnie dodaj swoje ograniczenia w ten sposób

addSubview(yourSubViewHere)
yourSubViewHere.translatesAutoresizingMaskIntoConstraints = false

addConstraint(NSLayoutConstraint(....
oskarko
źródło