Центрирование X субпредставления в автоматическом раскладке выдает "не подготовлен к ограничению"

82

У меня есть собственный подкласс UIView, который инициализируется пером.

В -awakeFromNib, я создаю подвид и пытаюсь центрировать его в надзоре.

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

Это прерывается и вызывает следующий вывод:

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>>
Патрик Перини
источник

Ответы:

95

Как указано в сообщении об ошибке, вы должны добавить ограничение к представлению, чтобы представления, участвующие в ограничении, были представлением, к которому вы его добавляете, или подвидом этого представления. В случае приведенного выше кода вы должны добавить это ограничение в self, а не в [self internalView]. Причина, по которой он не работает так, как написано, заключается в том, что одно из представлений, участвующих в ограничении ( self), не входит в иерархию представлений, если вы рассматриваете только [self internalView]и ниже).

Дополнительные сведения см. В разделе обсуждения документации по addConstraint:методу.

Вот ваша строка кода, исправленная, как я предлагаю:

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]];
Чарльз А.
источник
Это дает мне сообщение об ошибке Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. а затем он ломается<NSLayoutConstraint:0x126ad4c0 UIView:0xadd2e20.centerX == MyView:0xadd19b0.centerX>
Патрик Перини
1
Скорее всего, потому что вы не отключили перевод маски автоматического изменения размера в ограничения. Для этого я обновлю свой код выше. Вероятно, вам также потребуется добавить ограничения ширины / высоты, чтобы он был виден, и я бы рекомендовал добавить ограничение по центру Y или какое-то ограничение сверху / снизу для себя, чтобы сделать макет хорошо определенным.
Чарльз А.
поэтому у вас не может быть такого макета: @"V:|[v]|" :? как это относится к супервизору?
Сэм
1
@Sam Это допустимое ограничение, но оно не центрирует представление [v], представленное , оно даст вам верхние и нижние ограничения стандартного размера между [v]верхним и нижним краями и его супервизор. Думаю, вам также придется добавить его в супервизор.
Чарльз А.
8
Простое правило большого пальца: любые ограничения, относящиеся к размеру представления, должны быть добавлены к тому же представлению, а любые ограничения, относящиеся к его положению, должны быть добавлены к его родительскому виду.
Дипак GM
117

Для других, которые приходят сюда: я получил эту ошибку, потому что я добавил ограничения до того, как добавил вложенные представления в иерархию.

Дженни Теуниссен
источник
Спасибо, вы сэкономили мне много времени!
Стас
Это самая частая причина этой неудачи, которую я заметил до сих пор.
Аян Сенгупта,
именно и когда вы это сделаете, constraintWithItem: youViewпредмет в нем не существует!
Назир
36

Краткий ответ : поменяйте местами родительский и дочерний вид.
Предполагая, selfчто это родительский вид:

  • плохо :[subview addConstraint [NSLayoutConstraint constraintWithItem:self...

  • хорошо :[self addConstraint [NSLayoutConstraint constraintWithItem:subview...


Swift

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

Obj-C

SwiftАрхитектор
источник
10

Дополнительные советы:

У меня есть недавно разработанное приложение, которое отлично работает на iOS7, но выдает ошибку на iOS8 «Иерархия представлений не подготовлена ​​к ограничению».

Прочтите другой пост, в котором говорится, что (под) представления необходимо добавить до создания ограничений. Я сделал это, но все равно получил ошибку.

Затем я понял, что должен создавать ограничения в - (void) viewDidAppear вместо viewDidLoad

Кевин
источник
2
Когда viewDidAppear()вызывается, вид уже виден пользователю. При изменении ограничений он должен быть перед этим в viewWillLayoutSubviews(). Больше всего жизненного цикла здесь: medium.com/@SergiGracia/...
maganap
6

Это старый вопрос, но я хочу указать на эту строку из документации:

При разработке для iOS 8.0 или более поздней версии установите для свойства ограничения isActive значение true вместо прямого вызова метода addConstraint (_ :). Свойство isActive автоматически добавляет и удаляет ограничение из правильного представления.

По крайней мере, это устранит одну точку отказа, так как вам больше не нужно беспокоиться о вызове addConstraint в неправильном представлении.

WongWray
источник
2

В моем случае это было решено путем использования корневого представления в качестве контейнера ограничений вместо текущего созданного представления.

Я имею в виду, используйте внутри viewController

view.addConstraints([leftConstraintImage, topConstraintImage]) вместо imageView.addConstraints...

Вячеслав
источник
1

Как отмечает @CharlesA в своем ответе, проблема в том, что добавляемое вами представление не находится в надлежащей иерархии представлений. Его ответ хорош, но я собираюсь рассказать некоторые подробности о конкретном сценарии использования, который вызвал у меня эту проблему:

У меня это случилось, когда я пытался добавить ограничения к представлению, чей контроллер представления я создал с помощью instantiateViewControllerWithIdentifier. Для решения проблемы использовал [childViewController didMoveToParentViewController:self]. Это добавило представление в иерархию представлений, на которую ссылается мое ограничение.

БенволиоТ
источник
0

Сначала добавьте свой subView, а затем добавьте такие ограничения, как это

addSubview(yourSubViewHere)
yourSubViewHere.translatesAutoresizingMaskIntoConstraints = false

addConstraint(NSLayoutConstraint(....
оскарко
источник