Mac 应用中的程序化 NSLayoutConstraint 调整了 window contentView 的大小

programmatic NSLayoutConstraint in Mac app resizes window contentView

我正在尝试让自动布局在 Objective-C Mac 应用程序中以编程方式工作。

目标非常基本,一个简单的类似工具栏的视图横跨 window 顶部。高度不应改变,视图应位于 window 的顶部,宽度应随 window.

调整大小

当我在 .xib 中配置它时,效果很好。无需担心高度,我只需添加从自定义视图到超级视图(即 NSWindow 的 contentView)的前导、尾随和顶部约束连接。我不必更改任何优先级或其他设置。

我需要以编程方式执行此操作,在代码中创建 window、子视图和约束。我试过同时使用锚点和 NSLayoutConstraint 对象(均已参数化并基于视觉布局字符串)。在所有情况下,我都会遇到一个奇怪的行为,即 window 的 contentView 调整大小,降低到高度为零,并且没有任何内容被绘制到屏幕上。

这是一种完整的方法,使用锚点:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

// make window
NSRect windowRect = NSMakeRect(0.0, 0.0, 900.0, 200.0);
NSWindowStyleMask windowStyle = NSWindowStyleMaskTitled
    | NSWindowStyleMaskClosable
    | NSWindowStyleMaskResizable
    | NSWindowStyleMaskMiniaturizable;

NSWindow * win = [[NSWindow alloc] initWithContentRect:windowRect
                                                    styleMask:windowStyle
                                                      backing:NSBackingStoreBuffered
                                                        defer:NO];
win.title = @"Window Made in Code";
win.contentView.translatesAutoresizingMaskIntoConstraints = NO;

// add view
NSRect viewFrame = windowRect;
viewFrame.size.height = 80.0;
viewFrame.origin.y = 120.0;
NSView * view = [[MyView alloc] initWithFrame:viewFrame];
[win.contentView addSubview:view];
[view setNeedsDisplay:YES];

// add constraints
[view.leadingAnchor constraintEqualToAnchor:win.contentView.leadingAnchor].active = YES;
[view.trailingAnchor constraintEqualToAnchor:win.contentView.trailingAnchor].active = YES;
[view.topAnchor constraintEqualToAnchor:win.contentView.topAnchor].active = YES;
// note, the above also tried using layoutMargins of superview, same behavior

// show window
[win setIsVisible:YES];
[win center];
self.codeWindow = win;
[self.codeWindow makeKeyAndOrderFront:NSApp]; 

}

如果我不添加约束,一切都会正确绘制,但正如预期的那样,子视图不会随着 window.

调整大小

当我添加约束时,超级视图(window 的 contentView) 的大小调整为高度 0,因此不会绘制任何内容。如果我省略顶部约束,这不会发生,但是水平约束会向后工作:而不是在 window 调整大小时调整子视图的大小,它们会阻止 Window contentView 水平调整大小;我仍然可以将 window 拖动得更大,但调试时发现内容视图框架宽度没有增加。

这是我也尝试使用 NSLayoutConstraint 工厂方法的替代代码方法;我在这里设置优先级,但没有不同的优先级纠正行为:

NSLayoutConstraint * leadingC = [NSLayoutConstraint constraintWithItem:view
                                                             attribute:NSLayoutAttributeLeading
                                                             relatedBy:NSLayoutRelationEqual
                                                                toItem:view.superview
                                                             attribute:NSLayoutAttributeLeading
                                                            multiplier:1.0
                                                              constant:0.0];
leadingC.priority = NSLayoutPriorityDragThatCannotResizeWindow;
NSLayoutConstraint * trailingC = [NSLayoutConstraint constraintWithItem:view
                                                             attribute:NSLayoutAttributeLeading
                                                             relatedBy:NSLayoutRelationEqual
                                                                toItem:view.superview
                                                             attribute:NSLayoutAttributeLeading
                                                            multiplier:1.0
                                                              constant:0.0];
trailingC.priority = NSLayoutPriorityDragThatCannotResizeWindow;
NSLayoutConstraint * topC = [NSLayoutConstraint constraintWithItem:view
                                                             attribute:NSLayoutAttributeTop
                                                             relatedBy:NSLayoutRelationEqual
                                                                toItem:view.superview
                                                             attribute:NSLayoutAttributeTop
                                                            multiplier:1.0
                                                              constant:0.0];
[NSLayoutConstraint activateConstraints:@[leadingC, trailingC, topC]];                                                           
[view.superview addConstraints:self.constraints];

因为我尝试这几种不同的方法得到了同样糟糕的结果,所以我觉得我一定是完全遗漏了 NSLayoutConstraint 的某些东西,但我无法从教程或文档中弄清楚。我确实在运行时通过调试布局约束在 .xib 和代码创建的 windows 之间进行了比较,但无法从差异中重建我做错了什么,除了 .xib 超级视图具有 NSAutoresizingMaskLayoutConstraint 对象。

如果我注释掉 win.contentView.translatesAutoresizingMaskIntoConstraints = NO; 我确实会以编程方式调整大小行为,但这让我感到困惑 - 我认为当我们创建显式约束时我们应该将该标志设置为 NO?

感谢您就如何实现此功能提出任何建议!

当您选择加入约束时,您必须用约束完整地描述视图的大小。在这种情况下,您已经指定了工具栏的宽度,并设置了它的位置,但看起来您正试图用最初提供给视图的框架来固定高度。你还必须有一个约束来设置它的高度。

同时 translatesAutoresizingMaskIntoConstraints 描述了视图如何与其父视图相关,而不是与其子视图相关。所以你应该在工具栏视图上设置它,而不是 window 的 contentView。试试这个代码:

#import "AppDelegate.h"

@interface AppDelegate ()
@end

@interface MyView : NSView
@end
@implementation MyView

- (void)drawRect:(NSRect)dirtyRect
{
    CGContextRef cgContext = [[NSGraphicsContext currentContext] CGContext];

    CGContextSetFillColorWithColor(cgContext, [[NSColor yellowColor] CGColor]);
    CGContextFillRect(cgContext, self.bounds);
}

@end

@implementation AppDelegate
{
    NSWindowController *disjointController;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSRect windowRect = NSMakeRect(0.0, 0.0, 900.0, 200.0);
    NSWindowStyleMask windowStyle = NSWindowStyleMaskTitled
    | NSWindowStyleMaskClosable
    | NSWindowStyleMaskResizable
    | NSWindowStyleMaskMiniaturizable;

    NSWindow * win = [[NSWindow alloc] initWithContentRect:windowRect
                                                 styleMask:windowStyle
                                                   backing:NSBackingStoreBuffered
                                                     defer:NO];
    win.title = @"Window Made in Code";

    // add view
    NSView *view = [[MyView alloc] initWithFrame: windowRect];
    view.translatesAutoresizingMaskIntoConstraints = NO;
    view.layer.backgroundColor = [[NSColor yellowColor] CGColor];
    [win.contentView addSubview: view];

    // add constraints
    [view.leadingAnchor constraintEqualToAnchor: win.contentView.leadingAnchor].active = YES;
    [view.trailingAnchor constraintEqualToAnchor: win.contentView.trailingAnchor].active = YES;
    [view.topAnchor constraintEqualToAnchor: win.contentView.topAnchor].active = YES;
    [view.heightAnchor constraintEqualToConstant: 80].active = YES;

    // show window
    [win center];

    disjointController = [[NSWindowController alloc] initWithWindow: win];

    [win makeKeyAndOrderFront:NSApp];
}


- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}


- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
    return YES;
}
@end