ios 使用自动布局以编程方式添加视图给出“NSGenericException”,原因:“无法在视图上安装约束

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18060704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:36:28  来源:igfitidea点击:

Adding View Programatically With Auto Layout Gives 'NSGenericException', reason: 'Unable to install constraint on view

iosobjective-ccocoa-touchautolayout

提问by Gaurav Wadhwani

I am adding a view as a subview using [self.view addSubview:myView]. This works fine in portrait mode. However, it doesn't work at all in landscape. How do I add layout constraints programatically?

我正在使用[self.view addSubview:myView]. 这在纵向模式下工作正常。但是,它在横向上根本不起作用。如何以编程方式添加布局约束?

My view currently looks like portrait rectangle and I need it to look like landscape rectangle in landscape mode.

我的视图目前看起来像纵向矩形,我需要它在横向模式下看起来像横向矩形。

I tried this code to see how constraints in code work but it always results in an exception. The code is:

我尝试了这段代码来查看代码中的约束如何工作,但它总是导致异常。代码是:

[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];

This always results in an exception. I know the above code just attempts to ensure that the bottom of preview is 239px above the bottom of main view. But that doesn't work either.

这总是导致异常。我知道上面的代码只是试图确保预览底部高于主视图底部 239px。但这也行不通。

Could you help me out with sorting this so that I can resolve the landscape issue?

你能帮我整理一下,这样我就可以解决景观问题吗?

UPDATE

更新

The exception generated is:

产生的异常是:

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)

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)

I have added the subview before adding in the constraint so I am pretty sure the view is in hierarchy.

我在添加约束之前添加了子视图,所以我很确定视图是在层次结构中的。

UPDATE 2

更新 2

I set the parent view's property to `Autoresize Subviews' in IB. The subview now converts into landscape rectangle when the device is turned but its too narrow. I now need the code to make sure its of correct width maybe?

我在 IB 中将父视图的属性设置为“Autoresize Subviews”。当设备转动但它太窄时,子视图现在转换为横向矩形。我现在需要代码来确保它的宽度正确吗?

回答by Rob

A couple of observations:

几个观察:

  1. Your constraint references a toItemof self.view.superview. I assume you meant self.view.

  2. You're adding the constraint to _preView, but you should add it to self.view(if you make the above change; if not, you'd use self.view.superview). You always add the constraint to the nearest shared parent.

  3. For the views you're creating programmatically, make sure to set translatesAutoresizingMaskIntoConstraintsto NO.

    Thus:

    _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. 您的约束引用toItemself.view.superview。我想你的意思是self.view.

  2. 您将约束添加到_preView,但您应该将其添加到self.view(如果您进行上述更改;否则,您将使用self.view.superview)。您始终将约束添加到最近的共享父级。

  3. 对于您以编程方式创建的视图,请确保设置translatesAutoresizingMaskIntoConstraintsNO.

    因此:

    _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];
    


Chatting to you offline, two final observations:

线下跟你聊,最后观察两点:

  1. Your constraints were ambiguous. In the future, you can identify that by running the app in your debugger, hitting the pause button while the app is running (enter image description here) and then at the (lldb)prompt, you can enter

    po [[UIWindow keyWindow] _autolayoutTrace]

    ambiguous layout

    If you see AMBIGUOUS LAYOUT, then your constraints are not fully qualified (and thus you'll get unpredictable behavior). If you add the missing constraints, you should be able to eliminate this warning.

  2. If you want to animate constraint based views, you animate the changing of constantproperties of the constraints, not by changing frameproperties yourself. For example:

        // 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];
                             }];
        });
    
  1. 你的约束不明确。将来,您可以通过在调试器中运行应用程序,在应用程序运行时点击暂停按钮 ( 在此处输入图片说明) 然后在(lldb)提示符处输入

    po [[UIWindow keyWindow] _autolayoutTrace]

    模棱两可的布局

    如果您看到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];
                             }];
        });
    

回答by Hetal Vora

From your code above, there are 2 issues. 1. The constraint should be added to the parentview (self.view or self.view.superview as appropriate). 2. The items which are part of the myConstraint should be present in the view hierarchy to which you add your constraints.

从你上面的代码来看,有两个问题。1. 应将约束添加到父视图(根据需要添加 self.view 或 self.view.superview)。2. 属于 myConstraint 的项目应该出现在您添加约束的视图层次结构中。

My suggestion would be to check if your myConstraint can be formed with _preView and self.view , add the _preView to self.view as a subview and then add the myConstraint to self.view.

我的建议是检查您的 myConstraint 是否可以由 _preView 和 self.view 组成,将 _preView 添加到 self.view 作为子视图,然后将 myConstraint 添加到 self.view。

Also, the constraints should ideally be placed in -(void)updateConstraintsmethod in your view (if you have a custom view) and you should call [self setNeedsUpdateConstraints];in your view whenever you want the updateConstraints to be called on your view (after initializing your view, after rotation etc). You won't be calling updateConstraints directly.

此外,理想情况下,约束应该放在-(void)updateConstraints视图中的方法中(如果您有自定义视图),并且您应该[self setNeedsUpdateConstraints];在您希望在视图上调用updateConstraints 时调用您的视图(初始化视图后,旋转后等)。您不会直接调用 updateConstraints。

回答by Vignesh

There are few things about Auto layouts. When ever you add layout constraints make sure it is not ambiguous. Ambiguous layout would result in undefined behaviour in your display. So good idea is to use IB which will never allow you to create a ambiguous layout, but you got to go through all the constraints to make sure they are valid.

关于自动布局的事情很少。当您添加布局约束时,请确保它没有歧义。不明确的布局会导致显示中出现未定义的行为。使用 IB 是个好主意,它永远不会允许您创建模棱两可的布局,但是您必须经过所有约束以确保它们有效。

If you want to do it programatically I would suggest you to use Visual language.

如果您想以编程方式进行,我建议您使用Visual language

It will be helpful to go though these tipsbefore using layout.

在使用布局之前阅读这些提示会很有帮助。