【问题标题】:Is there a proper way to handle overlapping NSView siblings?是否有适当的方法来处理重叠的 NSView 兄弟姐妹?
【发布时间】:2009-01-21 17:44:51
【问题描述】:

我正在开发一个 Cocoa 应用程序,我遇到了一种情况,我希望两个 NSView 对象重叠。我有一个父 NSView,它包含两个子视图(NSView A 和 NSView B),每个子视图都可以有自己的几个子视图。

是否有适当的方法来处理这种重叠? NSView B 总是“高于” NSView A,所以我希望 NSView A 的重叠部分被屏蔽掉。

【问题讨论】:

    标签: cocoa


    【解决方案1】:

    克里斯,唯一的解决方案是使用 CALayers。这绝对是唯一的解决方案。

    NSViews 在 OSX(2010 年 9 月)中被彻底破坏:siblings 无法正常工作。一个或另一个将随机出现在顶部。

    重复一遍,问题出在兄弟姐妹上。

    对此进行测试:使用 NSViews 和/或 nsimageviews。制作一个视图是一个大图像(例如 1000x1000)的应用程序。在视图中,到处放三四个小图像/NSView。现在在上面放另一个 1000x1000 的大图像。反复构建和启动应用程序——你会发现它完全坏掉了。通常下面的(小)层会出现在大覆盖层的顶部。如果您在 NSViews 上打开 layer-backing,无论您尝试什么组合,它都无济于事。所以这是最终的测试。

    您必须放弃 NSViews 并使用 CALayers,仅此而已。

    CALayers 唯一的烦恼是你不能使用 IB 来设置你的东西。您必须在代码中设置所有图层位置,

    yy = [CALayer layer];
    yy.frame = CGRectMake(300,300, 300,300);
    

    只做一个 NSView,它的唯一目的是拿着你的第一个 CALayer(可能叫做'rear'),然后把你所有的 CALayer 放在后面。

    rear = [CALayer layer];
    rear.backgroundColor = CGColorCreateGenericRGB( 0.75, 0.75, 0.75, 1.0 );
    
    [yourOnlyNsView setLayer:rear]; // these two lines must be in this order
    [yourOnlyNsView setWantsLayer:YES]; // these two lines must be in this order
    
    [rear addSublayer:rr];
    [rear addSublayer:yy];
         [yy addSublayer:s1];
         [yy addSublayer:s2];
         [yy addSublayer:s3];
         [yy addSublayer:s4];
    [rear addSublayer:tt];
    [rear addSublayer:ff];
    

    然后一切都完美无缺,您可以嵌套和分组您想要的任何东西,并且无论您的结构多么复杂,所有内容都正确显示在应该显示在上方/下方的所有内容之上/之下,这一切都完美无缺。稍后你可以对图层做任何事情,或者以典型的方式随机播放,

    -(void) shuff
    {
    [CATransaction begin];
    [CATransaction setValue:[NSNumber numberWithFloat:0.0f]
                  forKey:kCATransactionAnimationDuration];
    if ..
        [rear insertSublayer:ff below:yy];
    else
        [rear insertSublayer:ff above:yy];
    [CATransaction commit];
    }
    

    (对于您所做的一切,令人讨厌的“零秒内”包装的唯一原因是阻止免费提供给您的动画 - 除非您想要动画!)

    顺便说一句,苹果的这句话,

    出于性能原因,Cocoa 会 不强制兄弟姐妹之间剪裁 意见或保证正确 失效和绘图行为时 兄弟视图重叠。

    他们下面的句子……

    如果你想绘制一个视图 在另一个视图的前面,你应该做 前视图一个子视图(或 后代)的后视图。

    在很大程度上是荒谬的(您不一定要用 subs 替换兄弟姐妹;并且上面测试中描述的明显错误仍然存​​在)。

    所以它是 CALayers!享受吧!

    【讨论】:

    • 感谢您的精彩回答,它让我了解了如何去除过去两年来我们软件中出现的可怕结块!
    • 经过一番调查后,我无法真正去除结块,但至少我现在对它有了更好的理解。干杯!
    • 有点晚了,但我正在玩重叠的兄弟视图并遇到了这个线程。如果重叠的兄弟 NSView 被认为是损坏的,那么 NSView 方法 addSubview:positioned:relativeTosortSubviewsUsingFunction:context 的意义何在?对于前者,文档指出:“在接收者的子视图中插入一个视图,使其立即显示在另一个视图的上方或下方。”如果 Cocoa 在绘图时没有强制执行排序,那么排序子视图有什么意义呢?事件处理?
    • “如果你想在另一个视图前面绘制一个视图,你应该让前视图成为后视图的子视图(或后代)。” 我相信兄弟排序纯粹是为了方便。 (不知道为什么。)
    【解决方案2】:

    如果您的应用程序仅支持 10.5,请为视图打开层,它应该可以正常工作。

    如果您打算支持 10.4 及更低版本,则需要找到一种不让视图重叠的方法,因为重叠的同级视图是未定义的行为。正如 View Programming Guide 所说:

    出于性能原因,Cocoa 不强制在同级视图之间进行剪辑,也不保证在同级视图重叠时正确的失效和绘制行为。如果你想在另一个视图前面绘制一个视图,你应该使前视图成为后视图的子视图(或后代)。

    我见过一些技巧,有时可以让它发挥作用,但这不是你可以依赖的东西。您需要将视图 A 设置为视图 B 的子视图,或者创建一个处理这两项职责的巨型视图。

    【讨论】:

    • @Joe:确实如此,我们在多个应用程序中使用了它。您是否为包含重叠子视图的 PARENT 视图启用了图层支持(重要)?这将使子视图自动支持图层,现在这些视图可以毫无问题地重叠。如何定义哪个视图在前面?在添加视图时使用 addSubview:positioned:relativeTo: (如果需要,重新添加它们)或者我认为操纵 z 排序也可以(这是一个 hack,你不应该为支持 NSViews 的层操纵它)。
    • 是的,它从 10.5 开始可以工作。一位苹果工程师在 2009 年表示,文档已经过时了。见stackoverflow.com/questions/10720062/…
    • 伙计们,您根本没有看清楚:这与 SIBLING 关系有关。不是亲子关系。干杯!
    【解决方案3】:

    有一种方法可以在不使用CALayers 的情况下做到这一点,而我一直在开发的应用程序可以证明这一点。创建两个窗口并使用它:

    [mainWindow addChildWindow:otherWindow ordered:NSWindowAbove];
    

    要删除“otherWindow”,请使用:

    [mainWindow removeChildWindow:otherWindow];
    
    [otherWindow orderOut:nil];
    

    你可能会想要使用窗口的标题栏:

    [otherWindow setStyleMask:NSBorderlessWindowMask];
    

    【讨论】:

      【解决方案4】:

      为了确保NSView B 始终与NSView A 重叠,请确保在添加subview: 时使用正确的NSWindowOrderingMode

      [parentView addSubview:B positioned:NSWindowAbove relativeTo:A];
      

      您还应该记住,如果视图 B 100% 不透明,则不会要求 A 的隐藏部分重绘。

      如果要移动子视图,还需要确保调用 -setNeedsDisplayInRect: 用于您正在发现的视图区域。

      【讨论】:

      • Nate,你在这里所说的与此无关。 Chris 询问的是 SIBLING 视图的具体情况,这在 OSX 中处理得非常糟糕。 (如果你从 iFone 迁移到 OSX,那真是令人气愤!)
      【解决方案5】:

      正如 Nate 所写,可以使用:

      self.addSubview(btn2, positioned: NSWindowOrderingMode.Above, relativeTo: btn1)
      

      但是,只要您通过“needDisplay = true”调用要求任一视图自行重绘,视图的顺序就不会得到尊重

      兄弟姐妹不会得到 drawRect 调用,只有视图直接层次结构会。

      更新 1

      为了解决这个问题,我不得不深入挖掘。大概一周的研究,我把我的发现传播到了几篇文章中。最后的突破在这篇文章中:http://eon.codes/blog/2015/12/24/The-odd-case-of-luck/

      更新 2

      请注意,虽然这个概念很难理解,但它很有效,而且效果很好。这是最终结果和支持它的代码,github repo 的链接等:http://eon.codes/blog/2015/12/30/Graphic-framework-for-OSX/

      【讨论】:

        猜你喜欢
        • 2012-05-30
        • 2019-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多