Добавление представления программно с автоматическим макетом дает "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 ответов


пара замечаний:

  1. ограничение ссылается на toItem of self.view.superview. Полагаю, вы имели в виду self.view.

  2. вы добавляете ограничение к _preView, но вы должны добавить его в self.view (если вы сделаете вышеуказанное изменение; если нет, вы бы использовали self.view.superview). Вы всегда добавляете ограничение к ближайшему общему родителю.

  3. для представлений, которые вы создаете программно, обязательно установите 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];
    

общения с вами в автономном режиме, два заключительных замечания:

  1. ваши ограничения были неоднозначными. В будущем вы можете определить это, запустив приложение в отладчике, нажав кнопку паузы во время работы приложения (enter image description here), а затем в (lldb) приглашения, вы можете ввести

    po [[UIWindow keyWindow] _autolayoutTrace]

    ambiguous layout

    если вы видите AMBIGUOUS LAYOUT, тогда ваши ограничения не полное (и, таким образом, вы получите непредсказуемое поведение). Если вы добавите отсутствующие ограничения, вы сможете устранить это предупреждение.

  2. если вы хотите анимировать представления на основе ограничений, вы анимируете изменение 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, который никогда не позволит вам создать неоднозначный макет, но вы должны пройти все ограничения, чтобы убедиться, что они действительны.

Если вы хотите сделать это программно, я бы посоветовал вам использовать визуальный язык.

Это будет полезно идти хоть этим советы перед использованием макета.