【问题标题】:How to make a custom view resize with the window with Cocoa Auto Layout?如何使用 Cocoa Auto Layout 使用窗口调整自定义视图的大小?
【发布时间】:2011-12-30 16:54:43
【问题描述】:

我有一个带有单个自定义视图的窗口,我希望自定义视图随窗口一起调整大小,以便它随时完全填满它。 如果我写:

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 自动布局系统,并且我想将它用于更复杂的情况(比如几个总是填满窗口的自定义视图)。

有谁知道出了什么问题以及我应该如何使用自动布局系统来获得我想要的结果?

【问题讨论】:

  • 这可能有助于调试:[[contentView constraints].window visualizeConstraints:contentView.constraints];
  • 我已经针对您所描述的情况测试了三种使用自动布局的不同方式,并且所有三种方式都有效。你的代码中一定有其他东西阻止自动布局按预期工作。你能上传一个能重现你的问题的最小测试用例吗?
  • 确实,它确实有效,但我找不到问题所在......我可能曾在将视图添加到超级视图之前应用了约束(但这确实会在控制台上输出错误我还没有看到它)。或者也许还有其他问题,通过提出问题,它让我从头开始重写所有内容,而没有最初的问题......

标签: cocoa layout


【解决方案1】:

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

关于超级视图的视觉格式限制

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

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

【讨论】:

  • 请注意,第三种方法是不完整的,并且会导致 SIGABRT,因为 ios 知道制作它的高度和宽度,但不知道实际放置它的位置!您需要在每个维度中提供两个 {start, finish, length}。
  • @BenjaminWheeler 是的,我认为你是对的。如果我不这样做,请随时编辑答案。
  • 谢谢。我也这样做了。它适合屏幕。但我无法调整 NSPanel 的大小。有什么想法吗?。
  • @Jbryson 我认为这是 Cocoa,而不是 Cocoa Touch,因此需要 NSView。顺便说一句,酷热传输应用程序......
  • @KKendall 感谢 NSView 提醒,我没有意识到上下文不是 iOS!
【解决方案2】:

@Bavarious 的回答很好,我再补充几句。

学习使用内置的调试支持非常重要!与许多开发一样,希望你总是在第一枪就做好一切是不现实的。这是自动布局的一个主要问题,因此我们在调试上投入了大量精力。步骤,简要:

  1. 确定位于错误位置的视图。从 gdb 调用方法 -[NSView _subtreeDescription] 和/或传递参数 -NSShowAllViews YES 可以帮助确定哪个视图是错误的。
  2. 确定错误或缺失的一个或多个约束。 -[NSLayoutConstraint constraintsAffectingLayoutForOrientation:] 有助于为您提供一组较小的约束来处理。 -[NSWindow VisualizeConstraints:] 可以帮助您了解那些约束,并且您可以从中看出哪些不是您想要的。它还会显示您的布局是否不明确(没有足够的约束)。
  3. 确定错误约束的来源。 Instruments 中的 Cocoa Layout 模板有点像 Leaks 工具 - 它会向您展示约束生命周期中的所有事件,例如它的创建位置、添加到窗口、修改等。所以一旦你知道了什么约束是问题,使用 Instruments 中的搜索字段过滤到仅查看该约束,您可以查看所有生命周期事件的回溯,并找出您在哪里做了您不想要的事情。

通常,您发布的那种问题(我的东西不起作用!)不足以让人们说出问题所在,这也是使用调试工具很重要的原因之一。有关更多信息,请参阅 WWDC 2011 会议视频(所有人免费)和文档。

Buuuuut 我真的可以知道这次出了什么问题。 :-) 在您关闭 translatesAutoresizingMaskIntoConstraints 之前,您受到的限制比您想要的要多 - 您的视图的宽度和高度也是固定的,这就是窗口无法调整大小的原因。但是,在您将其关闭后,您的布局就变得模棱两可,因为您没有将视图固定在任何东西上!你说过它应该有多大(与 superview 相同),但不是 where 它应该是。

Cocoa Frameworks,主要的自动布局作者

【讨论】:

  • 感谢您的回答,尤其是最后清晰的解释,帮助我理解出了什么问题。
  • Ken,只是想说自动布局方面的出色工作。我花了一段时间来掌握如何有效地使用它,但我已经能够用它做一些非常强大的事情。干杯!
【解决方案3】:

您可以使用易于阅读的布局锚创建约束:

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)

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 如果宽度或高度都为零,这也是正确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-21
    • 1970-01-01
    • 2011-10-06
    相关资源
    最近更新 更多