如何在Cocoa中绘制锥形线+椭圆形阴影

Bry*_*yan 9 macos cocoa objective-c

背景:

下面的镜头是OS X Lion中的Mail.app.当源列表变得太长时,在源列表底部的按钮上方会出现一条漂亮的阴影线.滚动时,源列表会在该阴影线下移动.当您展开窗口以使源列表中的所有内容都适合而不滚动时,阴影线将消失.

问题:

如何使用Cocoa绘制这条阴影线?我知道NSShadow等等,但在我看来,这里发生的事情不仅仅是阴影.有一条线巧妙地淡化到点(就好像你在Photoshop中的每一端应用了渐变蒙版.)同样,阴影是椭圆形的,当你接近线的末端时逐渐变细.所以它不只是常规的NSShadow,是吗?(它绝对不是图像,因为当您扩展源视图的宽度时,它可以很好地缩放.)

关于如何绘制这种形状的任何提示都将非常感激.

在此输入图像描述

对于坚持不懈的人来说,不,这并不违反NDA,因为Mail.app已被Apple公开展示.

Reg*_*ent 16

大概的概念:

.

  1. 创建一个带有尺寸150px × 10px的图层"图层A",
    并使用渐变填充它:
    • 较低的颜色: #535e71 不透明度: 33%
    • 上部颜色: #535e71 不透明度: 0%
  2. 创建一个带有尺寸150px × 1px的图层"图层B",
    并用实心不透明度填充它#535e71 : 50%
  3. "层A"和"层B" 组合成"层C".
  4. 应用反射梯度掩模#ffffff#000000到"层C".

视觉步骤:

在此输入图像描述

功能代码:

MyView.h:

#import <Cocoa/Cocoa.h>

@interface MyView : NSView {
@private

}

@end
Run Code Online (Sandbox Code Playgroud)

MyView.m:

#import "MyView.h"

@implementation MyView

- (CGImageRef)maskForRect:(NSRect)dirtyRect {
    NSSize size = [self bounds].size;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);

    CGContextClipToRect(context, *(CGRect*)&dirtyRect);

    CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height);

    size_t num_locations = 3;
    CGFloat locations[3] = { 0.0, 0.5, 1.0 };
    CGFloat components[12] = {
        1.0, 1.0, 1.0, 1.0,  // Start color
        0.0, 0.0, 0.0, 1.0,  // Middle color
        1.0, 1.0, 1.0, 1.0,  // End color
    };

    CGGradientRef myGradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, num_locations);

    CGPoint myStartPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGPoint myEndPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));

    CGContextDrawLinearGradient(context, myGradient, myStartPoint, myEndPoint, 0);

    CGImageRef theImage = CGBitmapContextCreateImage(context);
    CGImageRef theMask = CGImageMaskCreate(CGImageGetWidth(theImage), CGImageGetHeight(theImage), CGImageGetBitsPerComponent(theImage), CGImageGetBitsPerPixel(theImage), CGImageGetBytesPerRow(theImage), CGImageGetDataProvider(theImage), NULL, YES);

    [(id)theMask autorelease];

    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);

    return theMask;
}

- (void)drawRect:(NSRect)dirtyRect {
    NSRect nsRect = [self bounds];
    CGRect rect = *(CGRect*)&nsRect;
    CGRect lineRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, (CGFloat)1.0);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
    CGContextClipToRect(context, *(CGRect*)&dirtyRect);
    CGContextClipToMask(context, rect, [self maskForRect:dirtyRect]);

    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = {
        0.315, 0.371, 0.450, 0.3,  // Bottom color
        0.315, 0.371, 0.450, 0.0  // Top color
    };

    CGGradientRef myGradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, num_locations);

    CGPoint myStartPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGPoint myEndPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));

    CGContextDrawLinearGradient(context, myGradient, myStartPoint, myEndPoint, 0);

    CGContextSetRGBFillColor(context, 0.315, 0.371, 0.450, 0.5 );
    CGContextFillRect(context, lineRect);

    CGColorSpaceRelease(colorSpace);    
}

@end
Run Code Online (Sandbox Code Playgroud)

(我第一次使用纯粹的低级CoreGraphics,因此可能低于最佳值,可以进行改进.)

这是上面代码产生的实际屏幕截图:
在此输入图像描述
图纸延伸到视图的尺寸.

(我以前有两种技术显示在这里:"技术A"和"技术B".
"技术B"提供了优异的结果,并且实现起来也更简单,所以我放弃了"技术A".
有些评论可能仍然提到"技术A"虽然.只是忽略它们并享受功能完整的代码片段."

  • @tristan-seifert:`[window setBackgroundColor:[NSColor colorWithCalibratedHue:0.590饱和度:0.057亮度:0.890 alpha:1.000]];`;) (2认同)