Добавление представления программно с автоматическим макетом дает "NSGenericException", причина: "невозможно установить ограничение на представление
я добавляю представление в качестве подвида, используя [self.view addSubview:myView]
. Это прекрасно работает в портретном режиме. Однако в ландшафте это не работает. Как добавить ограничения макета программно?
мой вид в настоящее время выглядит как портретный прямоугольник, и мне нужно, чтобы он выглядел как ландшафтный прямоугольник в ландшафтном режиме.
я попробовал этот код, чтобы увидеть, как работают ограничения в коде, но это всегда приводит к исключению. Код:
[self.view addSubview:_preView];
NSLayoutConstraint *myConstraint = [NSLayoutConstraint
constraintWithItem:_preView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view.superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-239];
[_preView addConstraint:myConstraint];
это всегда приводит к исключение. Я знаю, что приведенный выше код просто пытается обеспечить, чтобы нижняя часть предварительного просмотра была 239px выше нижней части основного вида. Но это тоже не работает.
не могли бы вы помочь мне с сортировкой этого, чтобы я мог решить проблему ландшафта?
обновление
генерируется исключение:
2013-08-05 16:13:28.889 Sample Code[33553:c07] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view. Does the constraint reference something from outside the subtree of the view? That's illegal. constraint:<NSLayoutConstraint:0x912c430 UIView:0x8561340.bottom == UILayoutContainerView:0x8257340.bottom - 20> view:<UIView: 0x85774e0; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; autoresizesSubviews = NO; layer = <CALayer: 0x8577490>>'
*** First throw call stack:
(0x1a04012 0x173be7e 0x1a03deb 0x12ee4a0 0xbb983e 0xbb9a27 0xbb9b76 0xbb9d3b 0xbb9c4d 0x1c0d9 0x11395b3 0x19c3376 0x19c2e06 0x19aaa82 0x19a9f44 0x19a9e1b 0x24027e3 0x2402668 0x67fffc 0x2d3d 0x2c65)
libc++abi.dylib: terminate called throwing an exception
(lldb)
я добавил subview перед добавлением в ограничение, поэтому я уверен, что представление находится в иерархия.
обновление 2
Я установил свойство родительского представления в "Autoresize Subviews" В IB. Теперь subview преобразуется в ландшафтный прямоугольник, когда устройство повернуто, но слишком узкое. Теперь мне нужен код, чтобы убедиться, что его правильная ширина может быть?
3 ответов
пара замечаний:
ограничение ссылается на
toItem
ofself.view.superview
. Полагаю, вы имели в видуself.view
.вы добавляете ограничение к
_preView
, но вы должны добавить его вself.view
(если вы сделаете вышеуказанное изменение; если нет, вы бы использовалиself.view.superview
). Вы всегда добавляете ограничение к ближайшему общему родителю.-
для представлений, которые вы создаете программно, обязательно установите
translatesAutoresizingMaskIntoConstraints
кNO
.таким образом:
_preView.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:_preView]; NSLayoutConstraint *myConstraint = [NSLayoutConstraint constraintWithItem:_preView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-239]; [self.view addConstraint:myConstraint];
общения с вами в автономном режиме, два заключительных замечания:
-
ваши ограничения были неоднозначными. В будущем вы можете определить это, запустив приложение в отладчике, нажав кнопку паузы во время работы приложения (), а затем в
(lldb)
приглашения, вы можете ввестиpo [[UIWindow keyWindow] _autolayoutTrace]
если вы видите
AMBIGUOUS LAYOUT
, тогда ваши ограничения не полное (и, таким образом, вы получите непредсказуемое поведение). Если вы добавите отсутствующие ограничения, вы сможете устранить это предупреждение. -
если вы хотите анимировать представления на основе ограничений, вы анимируете изменение
constant
свойстваconstraints
, не изменяяframe
свойства себе. Например:// create subview UIView *subview = [[UIView alloc] init]; subview.backgroundColor = [UIColor lightGrayColor]; subview.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:subview]; // create dictionary for VFL commands NSDictionary *views = @{@"subview" : subview, @"superview" : self.view}; // add horizontal constraints [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|" options:0 metrics:nil views:views]]; // set the height of the offscreen subview to be the same as its superview [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[subview(==superview)]" options:0 metrics:nil views:views]]; // set the location of the subview to be just off screen below the current view NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.view.bounds.size.height]; [self.view addConstraint:constraint]; // then in two seconds, animate this subview back on-screen (i.e. change the top constraint `constant` to zero) double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ constraint.constant = 0.0; [UIView animateWithDuration:1.0 animations:^{ [self.view layoutIfNeeded]; }]; });
из вашего кода выше, есть 2 проблемы. 1. Ограничение должно быть добавлено в parentview (self.вид или самость.вид.superview по мере необходимости). 2. Элементы, являющиеся частью myConstraint, должны присутствовать в иерархии представлений, в которую добавляются ограничения.
мое предложение было бы проверить, может ли ваш myConstraint быть сформирован с _preView и self.просмотр, добавьте _preView в self.просмотр как подвида, а затем добавьте myConstraint в личность.вид.
кроме того, ограничения в идеале должны быть помещены в -(void)updateConstraints
метод в вашем представлении (если у вас есть пользовательский вид), и вы должны вызвать [self setNeedsUpdateConstraints];
в вашем представлении, когда вы хотите, чтобы updateConstraints вызывались в вашем представлении (после инициализации вашего представления, после вращения и т. д.). Вы не будете вызывать updateConstraints напрямую.
есть несколько вещей об автоматических макетах. Когда вы добавляете ограничения макета, убедитесь, что он не неоднозначен. Неоднозначный макет приведет к неопределенному поведению на вашем дисплее. Поэтому хорошая идея-использовать IB, который никогда не позволит вам создать неоднозначный макет, но вы должны пройти все ограничения, чтобы убедиться, что они действительны.
Если вы хотите сделать это программно, я бы посоветовал вам использовать визуальный язык.
Это будет полезно идти хоть этим советы перед использованием макета.