具有自动布局的 NSScrollView 在第一次手动调整窗口大小之前不会调整大小 [英] NSScrollView with auto layout not resizing until first manual window resize

查看:14
本文介绍了具有自动布局的 NSScrollView 在第一次手动调整窗口大小之前不会调整大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've got an OSX Cocoa app that has been built programatically (i.e., not with a NIB/XIB), which I'm trying to lay out using auto layout - but I'm getting some odd behaviour when the window first displays.

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

My main content is an NSView that holds has a collection of 100 NSButtons as subviews, laid out vertically. The buttons are all constrained relative to the NSView and each other; both the NSView and all the NSButtons have translatesAutoresizingMaskIntoConstraints=NO set. I believe the code for the content view is good (i.e., no ambiguous layouts, etc), because if I set the main window's contentView to the NSView, the buttons display as expected.

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

However, if I set the main window's contentView to be an NSScrollView, and set the documentView of the NSScrollView to be the NSView, I get display problems.

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

On first display, I get a blank window - no scroll bars, nothing:

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

The NSScrollView has translatesAutoresizingMaskIntoConstraints=NO. For debug purposes, I've also set the background colour of the NSScrollView to blue so that I can confirm what is being laid out where - but there's no blue shown anywhere.

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

But, as soon as I resize the window, the layout kicks in, and I get an NSScrollView the full size of the main window, with blue background, and scrollbars as expected:

我已经阅读了一些参考资料,这些参考资料表明问题在于作为 NSScrollView 的一部分的 clipView 缺乏约束.在此基础上,我尝试设置约束绑定 [NSScrollView contentView][NSScrollView documentView] 在垂直和水平方向(常数0,乘数1,开左、右、上和下).当我这样做时,NSScrollView 现在在第一次显示时可见,但它的大小错误.滚动不会滚动到内部内容的整个高度 - 可滚动内容会滚动,就好像它与可见窗口的大小相同.最后,内容与窗口的标题栏重叠:

I've read some references that suggest the problem is the lack of constraints on the clipView that is part of the NSScrollView. On that basis, I've tried setting up constraints binding [NSScrollView contentView] to [NSScrollView documentView] in the vertical and horizontal directions (with constant 0, multiplier 1, on the left, right, top and bottom). When I do this, the NSScrollView is now visible on first display, but it's the wrong size. The scroll doesn't scroll the full height of the internal content - the scrollable content scrolls as if it is the same size as the visible window. Lastly, the content overlaps the titlebar of the window:

同样,只要我调整窗口大小,约束就会启动,并且窗口会按我的预期显示(参见上一个屏幕截图).所以,我认为额外的约束并没有什么坏处,但它们似乎也没有添加任何东西.

Again, as soon as I resize the window, the constraints kick in, and the window displays as I'd expect (see the previous screenshot). So, I take it the extra constraints don't hurt, but they don't seem to be adding anything, either.

更令人困惑的问题 - 如果我完全关闭按钮,并且只使用没有子视图的空 NSView 作为内容视图,我会在启动时得到一个完整的蓝色窗口,正如我所期望的那样.

Further confusing matters - if I leave the buttons off altogether, and just use an empty NSView with no subviews as the content view, I get a full window of blue on startup, as I'd expect.

那么 - 这里发生了什么?感觉就像我错过了强制评估按钮约束的调用;是这样吗,还是这里发生了其他事情?

So - what's going on here? It feels like I'm missing a call to force the evaluation of constraints on the buttons; is that the case, or is something else going on here?

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

For those interested - here's my sample code. It's not Objective C - it's Python - but the language binding can convert Python method names into Objective C messages; the mapping to native ObjectiveC API should be obvious:

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()

推荐答案

我已经想出了一个答案 - 我对它并不完全满意,但它似乎有效.

I've worked out an answer - I'm not entirely happy with it, but it seems to work.

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

There are certain Cocoa widgets that don't deal well with autolayout - in particular, I've found problems with top level NSWindows and NSTabViewItems; I'm guessing other widgets might also be affected. Essentially, these are "container" widgets that have a top level "view" that must be set. If the "contained" widget is an NSScrollView (which itself will contain other widgets), the "container" widget has difficulty establishing a size for the "contained" scroll view.

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

The fix is to re-enable translatesAutoresizingMaskIntoConstraints for the view that will be used as the "contained" widget. In the example provided, the object scroll_view created on line 10 is the "contained" widget; the boolean value of the call to setTranslatesAutoresizingMaskIntoConstraints on line 15 should be True, not False.

这些问题在更新的 OS/X 版本中得到了改善 - Mavericks 没有 NSWindow 的问题,但它仍然存在 NSTabViewItem 的问题.但是,在较新版本的 OS X 上打开 translatesAutoresizingMaskIntoConstraints 似乎并没有造成任何损害;所有你都失去了 100% 自动布局解决方案的理论纯度.

These problems get better with more recent versions of OS/X - Mavericks doesn't have a problem with NSWindow, but it still has a problem with NSTabViewItem. However, it doesn't seem to do any damage to turn on translatesAutoresizingMaskIntoConstraints on newer versions of OS X; all you're losing it the theoretical purity of a 100% autolayout solution.

这篇关于具有自动布局的 NSScrollView 在第一次手动调整窗口大小之前不会调整大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆