层级托管的NSViews可以有子视图吗?

8

Layer-hosting NSViews(即您提供CALayer实例并使用setLayer:进行设置的NSView)可以包含子视图。为什么显然呢?因为在苹果自己的Cocoa Slides样例代码项目中,您可以勾选一个复选框,将AssetCollectionView从背景图层切换到托管图层:

- (void)setUsesQuartzCompositionBackground:(BOOL)flag {
    if (usesQuartzCompositionBackground != flag) {
        usesQuartzCompositionBackground = flag;

        /* We can display a Quartz Composition in a layer-backed view tree by 
           substituting our own QCCompositionLayer in place of the default automanaged 
           layer that AppKit would otherwise create for the view.  Eventually, hosting of 
           QCViews in a layer-backed view subtree may be made more automatic, rendering 
           this unnecessary.  To minimize visual glitches during the transition, 
           temporarily suspend window updates during the switch, and toggle layer-backed 
           view rendering temporarily off and back on again while we prepare and set the 
           layer.
        */
        [[self window] disableScreenUpdatesUntilFlush];
        [self setWantsLayer:NO];
        if (usesQuartzCompositionBackground) {
            QCCompositionLayer *qcLayer = [QCCompositionLayer compositionLayerWithFile:[[NSBundle mainBundle] pathForResource:@"Cells" ofType:@"qtz"]];
            [self setLayer:qcLayer];
        } else {
            [self setLayer:nil]; // Discard the QCCompositionLayer we were using, and let AppKit automatically create self's backing layer instead.
        }
        [self setWantsLayer:YES];
    }
}

在同一个AssetCollectionView类中,为每个应该显示的图片添加了子视图:
- (AssetCollectionViewNode *)insertNodeForAssetAtIndex:(NSUInteger)index {
    Asset *asset = [[[self assetCollection] assets] objectAtIndex:index];
    AssetCollectionViewNode *node = [[AssetCollectionViewNode alloc] init];
    [node setAsset:asset];
    [[self animator] addSubview:[node rootView]];
    [nodes addObject:node];

    return [node autorelease];
}

当我构建并运行应用程序并进行操作时,一切似乎都很好。
然而,在苹果公司的NSView类参考中关于setWantsLayer:方法中写道:

使用层托管视图时,您不应该依赖视图来绘制,也不应该向层托管视图添加子视图。

这是真的吗?示例代码是否不正确,只是偶然它能工作?还是文档是错误的(我怀疑)?或者因为子视图是通过动画代理添加的,所以没问题?
3个回答

20

当AppKit进行“图层托管”时,我们假设您可能(或可能不)拥有一整个子树的图层,而AppKit并不知道。

如果您向托管视图添加子视图,则它可能不会按照您想要的正确兄弟顺序出现。此外,我们有时会添加和删除它们,因此这可能会根据何时调用setLayer:、setWantsLayer:或视图添加或从父视图中删除而发生变化。在Lion(及之前的版本)上,当视图从窗口(或父视图)中移除时,我们会删除我们“拥有”的图层(即图层支持)。

添加子视图是可以的,但如果您有非NSViews的同级图层,则其子级兄弟顺序在子层数组中可能不是确定性的。


这在2019年仍然准确吗? - aleclarson
应该是这样的。如果他们更改了某些内容导致它无法工作,那么记录一个错误! - corbin dunn

1

我不知道这个问题的“正确”答案是什么。但我认为CocoaSlides示例在文档所说的“不应该”做的范围内工作。在示例中,查看何时调用insertNodeForAssetAtIndex:方法,您会发现它仅在视图被填充之前,并且在其分配图层或调用setWantsLayer:之前发生。

文档并没有说一个带有图层的视图不能包含任何子视图,它们只是说您不能将任何子视图添加到其中。在添加这些子视图的时候,主视图还没有成为一个带有图层的视图。在手动创建图层分配给它后,它已经变成了一个带有图层的视图,不再添加更多的子视图。

因此,文档和这个特定的示例之间实际上没有矛盾。话虽如此,进一步探索这个问题可能会很有趣,例如通过在initWithFrame:中插入[self setUsesQuartzCompositionBackground:YES];来从一开始就打开QC背景层。

剧透警告: 看起来一切都很正常。由于所有的QC动画,显示器的创建速度有点慢(这并不奇怪),但除此之外,一切都很顺利。


谢谢,杰克!关于“添加”与“已经有”的子视图的观察很有趣。也许文档已经过时了,因为它们也涉及到这个问题:https://dev59.com/lmgv5IYBdhLWcg3wSewe#10720422 - Johannes Fahrenkrug

0
关于这段来自苹果的代码,有一个评论:它是有问题的。
当你第一次启动应用程序时,请注意漂亮的渐变背景。打开QC,然后关闭。
噼里啪啦,没有了渐变背景。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接