【问题标题】:Adding View Programatically With Auto Layout Gives 'NSGenericException', reason: 'Unable to install constraint on view使用自动布局以编程方式添加视图会给出“NSGenericException”,原因:“无法在视图上安装约束
【发布时间】:2013-08-05 14:28:26
【问题描述】:

我正在使用[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)

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

更新 2

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

【问题讨论】:

标签: ios objective-c cocoa-touch autolayout


【解决方案1】:

几个观察:

  1. 您的约束引用了 toItemself.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. 你的限制是模棱两可的。以后,您可以通过在调试器中运行应用程序来识别这一点,在应用程序运行时点击暂停按钮 (),然后在(lldb) 提示符下,您可以输入

    po [[UIWindow keyWindow] _autolayoutTrace]

    如果您看到AMBIGUOUS LAYOUT,那么您的约束不是完全限定的(因此您会得到不可预知的行为)。如果您添加缺少的约束,您应该能够消除此警告。

  2. 如果您想为基于约束的视图设置动画,您可以为constraintsconstant 属性的更改设置动画,而不是通过自己更改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];
                             }];
        });
    

【讨论】:

  • 虽然应用程序没有崩溃,但它在控制台中给出了这个:dl.dropboxusercontent.com/u/90817764/op.jpg 并且应用的约束甚至不起作用。
  • @GauravWadhwani 正如我在上面的评论中提到的(并且在您与我们分享的这条消息中提到),您应该将translatesAutosizingMaskIntoConstraints 设置为NO,以便您以编程方式创建的视图自己指定约束。默认情况下,iOS 会假定它必须为您创建约束,并且这些约束似乎与您尝试添加的约束相冲突。因此,将 set translatesAutosizingMaskIntoConstraints 设置为 NO,您不会与任何自动生成的约束有任何冲突(因为不会有任何冲突)。
  • 所以我应该设置为 [self.view setTranslatesAutoresizingMaskIntoConstraints:NO]; ?
  • @GauravWadhwani 您这样做是针对您以编程方式创建的视图,而不是要添加它们的视图。
  • 嘿@Rob,感谢您的回复。我设置 [_preView setTranslatesAutoresizingMaskIntoConstraints:NO];但是,这会导致 _preView 在屏幕上不可见。我在控制台上记录了它的起源并显示了可见的坐标。但是屏幕上什么都没有:(当我评论那条线时,视图再次出现在屏幕上。
【解决方案2】:

根据您上面的代码,有 2 个问题。 1.约束应该添加到父视图(self.view或self.view.superview视情况而定)。 2. 作为 myConstraint 一部分的项目应该出现在您添加约束的视图层次结构中。

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

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

【讨论】:

  • 所以我添加了这个方法 -(void)updateConstraints { NSLayoutConstraint *myConstraint = [NSLayoutConstraint constraintWithItem:_preView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view.superview attribute:NSLayoutAttributeBottom multiplier:1.0 constant:- 20]; [self.view addConstraint:myConstraint];我在动画加载后调用该方法: [self.view setNeedsUpdateConstraints];但它仍然会导致异常
  • 正如我所提到的,您在 myConstraint 中引用的视图应该是您添加约束的视图层次结构的一部分。因此,如果您的 myConstraint 用于 preView 和 self.view.superview,则必须将您的约束添加到 self.view.superview。在调用约束之前,preView 应该已经添加到 self.view 或 self.view.superview 中。
  • 您遇到了什么异常?请同时提供。
  • 基于异常,问题就是我所描述的。您试图对添加约束(_preView 或 self.view)的视图树之外的东西(self.view.superview)使用约束。将约束添加到 self.view.superview 或将 myConstraint 修改为介于 _preView 和 self.view 之间并将约束添加到 self.view。
  • 我不确定我是否理解您的最后一行。抱歉,你能用代码解释一下吗? :(
【解决方案3】:

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

如果您想以编程方式执行此操作,我建议您使用Visual language

在使用布局之前查看这些tips 会很有帮助。

【讨论】:

  • 我不能使用 IB,因为视图是以编程方式添加到主窗口的。会看到视觉语言
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多