在第一个手动窗口调整大小之前,自动布局的NSScrollView不会调整大小

fre*_*742 3 cocoa nsscrollview autolayout

我有一个OSX Cocoa应用程序,它是以编程方式构建的(即,不是使用NIB/XIB),我正在尝试使用自动布局进行布局 - 但是当窗口首次显示时,我会得到一些奇怪的行为.

我的主要内容是一个NSView,其中包含100个NSButton作为子视图的集合,垂直布局.这些按钮都相对于NSView和彼此都受到约束; NSView和所有NSButtons都已translatesAutoresizingMaskIntoConstraints=NO设置.我相信内容视图的代码是好的(即没有模糊的布局等),因为如果我将主窗口的contentView设置为NSView,按钮会按预期显示.

但是,如果我将主窗口的contentView设置为NSScrollView,并将NSScrollView的documentView设置为NSView,则会出现显示问题.

在第一次显示时,我得到一个空白窗口 - 没有滚动条,没有:

初始窗口显示

NSScrollView有translatesAutoresizingMaskIntoConstraints=NO.出于调试目的,我还将NSScrollView的背景颜色设置为蓝色,以便我可以确认在哪里布局 - 但是在任何地方都没有显示蓝色.

但是,只要我调整窗口大小,布局就会启动,我会得到一个NSScrollView主窗口的完整大小,蓝色背景和滚动条按预期方式:

窗口调整大小后显示

我已经阅读了一些引用,表明问题是对作为NSScrollView一部分的clipView缺乏约束.在此基础上,我试图建立约束结合[NSScrollView contentView][NSScrollView documentView]在垂直和水平方向(与常数0,乘法器1,在左侧,右侧,顶部和底部).当我这样做时,NSScrollView现在在第一次显示时可见,但它的大小错误.滚动不会滚动内部内容的整个高度 - 可滚动内容会滚动,就好像它与可见窗口的大小相同.最后,内容与窗口的标题栏重叠:

初始显示,带有NSScrollView约束

同样,一旦我调整窗口大小,约束就会启动,窗口会显示为我所期望的(请参阅上一个屏幕截图).所以,我认为额外的限制没有受到伤害,但它们似乎也没有添加任何东西.

更令人困惑的事情 - 如果我完全不使用按钮,只使用没有子视图的空NSView作为内容视图,我会在启动时获得完整的蓝色窗口,正如我所期望的那样.

那么 - 这里发生了什么?感觉我错过了一个强制评估按钮约束的调用; 就是这种情况,或者是其他事情在这里发生了什么?

对于那些感兴趣的人 - 这是我的示例代码.它不是Objective C - 它是Python - 但是语言绑定可以将Python方法名称转换为Objective C消息; 原生ObjectiveC API的映射应该是显而易见的:

app = NSApplication.sharedApplication()
app.setActivationPolicy_(NSApplicationActivationPolicyRegular)

main_window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
    NSMakeRect(100, 100, 640, 480),
    NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask,
    NSBackingStoreBuffered,
    False)

scrollview = NSScrollView.alloc().init()
scrollview.setHasVerticalScroller_(True)
scrollview.setHasHorizontalScroller_(True)
scrollview.setAutohidesScrollers_(True)
scrollview.setBorderType_(NSNoBorder)
scrollview.setTranslatesAutoresizingMaskIntoConstraints_(False)

scrollview.backgroundColor = NSColor.blueColor()

container = NSView.alloc().init()
container.setTranslatesAutoresizingMaskIntoConstraints_(False)

buttons = [
    NSButton.alloc().init()
    for b in range(0, 100)
]

for i, button in enumerate(buttons):
    button.setBezelStyle_(NSRoundedBezelStyle)
    button.setButtonType_(NSMomentaryPushInButton)
    button.setTitle_(get_NSString('Button %s' % i))
    button.setTranslatesAutoresizingMaskIntoConstraints_(False)

    container.addSubview_(button)

    if i == 0:
        container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
            button, NSLayoutAttributeTop,
            NSLayoutRelationEqual,
            container, NSLayoutAttributeTop,
            1, 50,
        ))
    else:
        container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
            button, NSLayoutAttributeBottom,
            NSLayoutRelationEqual,
            buttons[i-1], NSLayoutAttributeBottom,
            1, 50,
        ))

    container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
        button, NSLayoutAttributeLeft,
        NSLayoutRelationEqual,
        container, NSLayoutAttributeLeft,
        1, 50,
    ))

    container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
        button, NSLayoutAttributeRight,
        NSLayoutRelationEqual,
        container, NSLayoutAttributeRight,
        1, -50,
    ))

container.addConstraint_(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_(
    buttons[-1], NSLayoutAttributeBottom,
    NSLayoutRelationEqual,
    container, NSLayoutAttributeBottom,
    1, -50,
))

scrollview.setDocumentView_(container)

main_window.setContentView_(scrollview)

main_window.makeKeyAndOrderFront_(None)

app.activateIgnoringOtherApps_(True)
app.run()
Run Code Online (Sandbox Code Playgroud)

fre*_*742 8

我找到了答案 - 我对它并不完全满意,但似乎有效.

某些Cocoa小部件不能很好地处理自动布局 - 特别是,我发现了顶级NSWindows和NSTabViewItems的问题; 我猜其他小部件也可能受到影响.基本上,这些是具有必须设置的顶级"视图"的"容器"小部件.如果"包含"窗口小部件是NSScrollView(它本身将包含其他窗口小部件),则"容器"窗口小部件很难为"包含"滚动视图建立大小.

修复方法是重新启用 translatesAutoresizingMaskIntoConstraints将用作"包含"窗口小部件的视图.在提供的示例中,scroll_view在第10行创建的对象是"包含"小部件; 第setTranslatesAutoresizingMaskIntoConstraints15行的调用的布尔值应该是True,而不是False.

对于最新版本的OS/X,这些问题会变得更好 - 小牛队没有问题NSWindow,但它仍然存在问题NSTabViewItem.但是,打开translatesAutoresizingMaskIntoConstraints较新版本的OS X 似乎没有任何损害; 所有你都失去了100%自动布局解决方案的理论纯度.