NSScrollView:像 Messages.app 这样的顶部边框淡入淡出

NSScrollView: fade in a top-border like Messages.app

我想做什么:

在 OS 10.10 的 Messages.app 中,当您向上滚动最左侧的窗格(对话列表)时,一条漂亮的水平线会在大约 0.5 秒后逐渐消失。当您向下滚动时,该行会淡出。


我有什么:

我正在尝试在我自己的应用程序中实现这种效果,而且我已经非常接近了。我将 NSScrollView 子类化并完成了以下操作:

- (void) awakeFromNib
{
    _topBorderLayer = [[CALayer alloc] init];

    CGColorRef bgColor = CGColorCreateGenericGray(0.8, 1.0f);
    _topBorderLayer.backgroundColor = bgColor;
    CGColorRelease(bgColor);

    _topBorderLayer.frame = CGRectMake(0.0f, 0.0f, self.bounds.size.width, 1.0f);
    _topBorderLayer.autoresizingMask = kCALayerWidthSizable;
    _topBorderLayer.zPosition = 1000000000;

    _fadeInAnimation = [[CABasicAnimation animationWithKeyPath:@"opacity"] retain];
    _fadeInAnimation.duration = 0.6f;
    _fadeInAnimation.fromValue = @0;
    _fadeInAnimation.toValue = @1;
    _fadeInAnimation.removedOnCompletion = YES;
    _fadeInAnimation.fillMode = kCAFillModeBoth;

    [self.layer insertSublayer:_topBorderLayer atIndex:0];
}
- (void) layoutSublayersOfLayer:(CALayer *)layer
{
    NSPoint origin = [self.contentView documentVisibleRect].origin;

    // 10 is a fudge factor for blank space above first row's actual content
    if (origin.y > 10)
    {
        if (!_topBorderIsShowing)
        {
            _topBorderIsShowing = YES;
            [_topBorderLayer addAnimation:_fadeInAnimation forKey:nil];
            _topBorderLayer.opacity = 1.0f; 
        }
    }
    else
    {
        if (!_topBorderIsShowing)
        {
            _topBorderIsShowing = NO;
            // Fade out animation here; omitted for brevity
        }
    }
}

问题

我添加的 "border" 子层没有绘制在 ScrollView 中所有其他内容之上,所以我们最终得到这个:

我的 outlineView 这一行中图像、文本字段和复选框周围的框架是 "overdrawing" 我的边框层。

这是什么原因造成的

我认为这是因为 scrollView 包含在启用了 Vibrancy 的 NSVisualEffectView 中。我认为这是因为如果我将 "border" 子层的颜色更改为 100% 黑色,这个问题就会消失。同样,如果我在 OS X 的系统偏好设置 > 辅助功能中打开 "Reduce Transparency",问题就会消失。

我认为 Vibrancy 合成正在使用我的灰色边框子图层和代表 outlineView 行中每个组件的图层并混淆颜色。

所以...我如何停止单层?我已经尝试了各种方法来克服这个问题。我觉得我已经完成了 99% 的可靠实施,但无法解决最后一个问题。有人可以帮忙吗?


注意:

我知道在支持层的环境中直接处理层是危险的。 Apple 的文档清楚地表明,如果我们使用图层支持,我们将无法更改视图图层的某些属性。然而:添加和删除子层(就像我一样)不是被禁止的行为。

更新:

这个答案虽然有效,但如果您使用的是 AutoLayout,就会出现问题。在调用 Layout 之后,您将开始收到 scrollView 仍然需要更新的警告,因为在更新过程中某些东西弄脏了布局。我还没有找到解决方法。


原解:

解决这个问题的最简单方法就是将 contentView 插入边框子层的高度:

- (void) tile
{
    id contentView = [self contentView];
    [super tile];
    [contentView setFrame:NSInsetRect([contentView frame], 0.0, 1.0)];
}

几个小时前就应该想到了。效果很好。我会将问题留给可能希望实现这些漂亮的淡化边界的任何人。