如何使用Cocoa Auto Layout窗口调整自定义视图的大小?

时间:2011-11-16 18:33:44

标签: cocoa layout

我有一个窗口,其中包含一个自定义视图,我希望自定义视图可以随窗口调整大小,以便随时完全填充它。 如果我写:

NSView *contentView = [self.window contentView];
CustomView *customView = [[CustomView alloc] initWithFrame:[contentView bounds]];
[contentView addSubview:customView];
[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeWidth
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeWidth
        multiplier:1
        constant:0]];
[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeHeight
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeHeight
        multiplier:1
        constant:0]];

然后窗口不让我调整大小。
如果我添加:

[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

然后不显示自定义视图(似乎永远不会调用drawRect:)。 我尝试了不同的方式(包括视觉格式@"|[customview]|"),但我总是遇到同样的问题。我知道可以使用旧的自动调整系统来完成:

[customView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];

但是我想使用Cocoa Auto Layout系统,我想将它用于更复杂的情况(比如总是填满窗口的几个自定义视图)。

有谁知道什么是错的,我应该如何使用自动布局系统来获得我想要的结果?

4 个答案:

答案 0 :(得分:107)

使用自动布局,有(至少)三种可能的方式来约束视图,使其占据整个窗口的内容视图,并在适当时调整大小。

关于superview的视觉格式约束

NSView *contentView = [_window contentView];
MyView *customView = [[MyView alloc] initWithFrame:[contentView bounds]];
[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

[contentView addSubview:customView];

NSDictionary *views = NSDictionaryOfVariableBindings(customView);

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

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

边缘的编程约束

(这应该等同于上面的可视格式)

+ (void)addEdgeConstraint:(NSLayoutAttribute)edge superview:(NSView *)superview subview:(NSView *)subview {
    [superview addConstraint:[NSLayoutConstraint constraintWithItem:subview
        attribute:edge
        relatedBy:NSLayoutRelationEqual
        toItem:superview
        attribute:edge
        multiplier:1
        constant:0]];
}

NSView *contentView = [_window contentView];
MyView *customView = [[MyView alloc] initWithFrame:[contentView bounds]];
[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

[contentView addSubview:customView];

[[self class] addEdgeConstraint:NSLayoutAttributeLeft superview:contentView subview:customView];
[[self class] addEdgeConstraint:NSLayoutAttributeRight superview:contentView subview:customView];
[[self class] addEdgeConstraint:NSLayoutAttributeTop superview:contentView subview:customView];
[[self class] addEdgeConstraint:NSLayoutAttributeBottom superview:contentView subview:customView];

大小

的编程约束
NSView *contentView = [_window contentView];
MyView *customView = [[MyView alloc] initWithFrame:[contentView bounds]];
[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

[contentView addSubview:customView];

[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeWidth
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeWidth
        multiplier:1
        constant:0]];
[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeHeight
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeHeight
        multiplier:1
        constant:0]];

第三种方法是问题中列出的方法,如果存在进一步的限制,它可能无效。例如,没有:

[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

也会应用原始自动调整大小的掩码,这会导致问题中描述的行为:窗口未调整大小。

如Regexident所述,您可以使用:

[_window visualizeConstraints:[contentView constraints]];

调试自动布局。值得检查控制台输出。

答案 1 :(得分:47)

@ Bavarious的答案很好,我只想补充几点。

学习使用内置的调试支持非常重要!正如许多发展一样,希望你在第一次拍摄时始终能够做到一切都是不现实的。这是自动布局的一个主要问题,因此我们花了很多精力进行调试。步骤,简要说明:

  1. 确定位置错误的视图。从gdb调用方法 - [NSView _subtreeDescription]和/或传递参数-NSShowAllViews YES可以帮助识别哪个视图是错误的。
  2. 确定错误或缺失的约束或约束。 - [NSLayoutConstraint constraintsAffectingLayoutForOrientation:]有助于为您提供一组较小的约束。 - [NSWindow visualizeConstraints:]可以帮助您了解那些 的约束条件,您可以从中看到哪些不是您想要的东西。它还会显示您的布局是否模糊(没有足够的约束)。
  3. 确定错误约束的来源。 Instruments中的Cocoa Layout模板有点像Leaks仪器 - 它会向你显示约束生命周期中的所有事件,比如它的创建位置,添加到窗口,修改等等。所以一旦你知道了什么约束是问题,使用Instruments中的搜索字段过滤到只查看该约束,您可以看到所有生命周期事件的回溯,并找出你做了一些你不想要的事情。
  4. 通常你发布的问题(我的东西不起作用!)不足以让人们知道什么是错的,这是使用调试内容的重要原因之一。有关详细信息,请参阅WWDC 2011会话视频(全民免费)和文档。

    Buuuuut我实际上可以告诉这次出了什么问题。 :-)在关闭translatesAutoresizingMaskIntoConstraints之前,你比你想要的更受约束 - 视图的宽度和高度也是固定的,这就是窗口无法调整大小的原因。你把它关掉后,你的布局模糊不清,因为你没有把视线固定在任何东西上!你曾经说过它应该有多大(和superview一样),但不是它应该是

    Cocoa Frameworks,主要自动布局作者

答案 2 :(得分:6)

您可以使用非常容易阅读的格式创建布局锚点约束:

Swift2代码

func fit(childView: UIView, parentView: UIView) {
    childView.translatesAutoresizingMaskIntoConstraints = false
    childView.topAnchor.constraintEqualToAnchor(parentView.topAnchor).active = true
    childView.leadingAnchor.constraintEqualToAnchor(parentView.leadingAnchor).active = true
    childView.trailingAnchor.constraintEqualToAnchor(parentView.trailingAnchor).active = true
    childView.bottomAnchor.constraintEqualToAnchor(parentView.bottomAnchor).active = true
}

使用:

parrentView.addSubview(childView)
fit(childView, parentView: parrentView)

答案 3 :(得分:0)

我发现-drawRect:在框架矩形为0,0,0,0的情况下不会被调用。不受欢迎的约束似乎导致框架变为0,0,0,0。