在 ViewController 的 loadView 中创建的自动调整视图的 AutoLayout 约束

Posted

技术标签:

【中文标题】在 ViewController 的 loadView 中创建的自动调整视图的 AutoLayout 约束【英文标题】:AutoLayout constraints for a autoresizing view created in ViewController's loadView 【发布时间】:2013-04-28 09:37:03 【问题描述】:

我的UIViewController 通过覆盖 loadView 方法来创建它的视图:

- (void)loadView 
    UIView *view = [[UIView alloc] init];
    view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
    self.view = view;

现在我想切换到 AutoLayout 并因此添加一个

view.translatesAutoresizingMaskIntoConstraints = NO;

到 loadView 方法。现在我必须指定之前自动生成的相同约束。我的方法是用

覆盖 updateViewConstraints
- (void)updateViewConstraints 
    if (0 == [[self.view constraints] count]) 
        NSDictionary* views = @@"view" : self.view;

        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:0 views:views]];
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:0 views:views]];
     

    [super updateViewConstraints];

但我得到一个例外,因为我认为这种约束应该与超级视图一起使用:

*** 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.

那么,正确的 Contraints 应该是什么样子的?

【问题讨论】:

【参考方案1】:

你需要在 superview 上设置约束。异常是通过传递“|”引用superview引起的以视觉形式。如果您像下面这样更新代码,它将起作用:

- (void)updateViewConstraints 
    if (self.view.superview != nil && [[self.view.superview constraints] count] == 0) 
        NSDictionary* views = @@"view" : self.view;

        [self.view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:0 views:views]];
        [self.view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:0 views:views]];
    
   [super updateViewConstraints];

在实践中,您可能希望检查父视图上的 0 约束以外的其他内容,但这应该会有所帮助。

【讨论】:

【参考方案2】:

您不必像 Matt Neuburg 解释的那样在根视图上设置约束 Chapter 19 of his Programming ios 6 book, in section Manual Layout:

我们没有费心给我们的观点(self.view)一个合理的框架。这是因为我们依赖其他人来适当地构建视图。在这种情况下,“其他人”是窗口,它响应将其rootViewController 属性设置为视图控制器,方法是将视图控制器的视图适当地框定为根视图,然后将其作为子视图放入窗口。

【讨论】:

【参考方案3】:

CEarwood 方法的问题在于,这是一个 ViewController,它的视图不是任何其他视图的子视图,因此调用 self.view.subview 只会导致 nil。请记住,Apple 文档和指南强烈建议 UIViewController 或多或少地占据整个屏幕(除了导航栏或标签栏等)。

Palimondo 的回答基本上是正确的:你的 UIViewController 需要在 loadView 中初始化它的视图,但它不需要指定它的框架或约束,因为它们会自动设置为窗口的框架和约束。如果您自己不实现 loadView,这正是默认情况下所做的。

【讨论】:

有个小错误:你说的是self.view.superview而不是self.view.subview【参考方案4】:

我不确定您是否需要为窗口的根视图设置约束。

也就是说,你的约束看起来是正确的,我认为你得到的例外是因为:

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:0 views:views]];

使用 |表示视图的超级视图的符号。作为根级视图,它没有超级视图。这样的事情可能会更好:

- (void)loadView 
    UIView *customView = [[UIView alloc] init];
    [self.view addSubview:customView];
    NSDictionary* views = @@"customView" : customView;

    [customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|" options:0 metrics:0 views:views]];
    [customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" options:0 metrics:0 views:views]];

【讨论】:

但我明确不想将视图添加为子视图,因为这样我就会有一个我不需要的未使用视图。 拥有一个额外的、未使用的视图有多重要?我只是在这里问,这是你的问题。您是否尝试过放弃约束?通过 Storyboard 的默认视图控制器有哪些约束? 视图是以编程方式创建的,所以没有来自情节提要。额外的视图会浪费 cpu 时间和内存。 好吧,如果这对您来说很重要,那么听起来您需要跳过根视图的自动布局。 "与窗口的根视图控制器关联的视图根据窗口的特性获取一个框架。" - developer.apple.com/library/ios/#featuredarticles/…

以上是关于在 ViewController 的 loadView 中创建的自动调整视图的 AutoLayout 约束的主要内容,如果未能解决你的问题,请参考以下文章

iOS之深入了解控制器View的加载

在不同的 ViewController 中切换到新的 ViewController?

在调用 viewController 之前调用 ViewController 方法的问题

如何在使用 Swift 关闭 ViewController 期间将值从 ViewController B 传递给 ViewController A? [复制]

在 SWIFT 语言的 Storyboard 中将值从一个 ViewController 传递到另一个 ViewController

如何在另一个 ViewController 中将带有 Xib 的 ViewController 显示为 SubView