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

时间:2011-06-16 02:02:10

标签: objective-c cocoa macos

背景

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

问题:

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

有关如何绘制此形状的任何提示将不胜感激。

enter image description here

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

2 个答案:

答案 0 :(得分:16)

一般理念:

  1. 使用尺寸150px × 10px 创建“图层A”图层 并使用 Gradient 填充:
    • 较低的颜色#535e71 不透明度 33%
    • 上层颜色 #535e71 不透明度: 0%
  2. 使用尺寸150px × 1px 创建“图层B”图层 并使用实心#535e71 不透明度填充: 50%
  3. 将“图层A”和“图层B”一起组合成“图层C”。
  4. 反射渐变遮罩#ffffff应用到#000000到“C层”。
  5. 视觉步骤:

    enter image description here

    功能代码:

    <强> MyView.h

    #import <Cocoa/Cocoa.h>
    
    @interface MyView : NSView {
    @private
    
    }
    
    @end
    

    <强> 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
    

    (我第一次使用纯粹的低级CoreGraphics,因此可能是次优的,可以进行改进。)

    这是上面代码产生的实际屏幕截图:
    enter image description here
    图纸延伸到视图的尺寸。

    (我以前有两种技术:“技术A”和“技术B” “技术B”提供了优异的结果,并且实施起来也更简单,所以我放弃了“技术A” 有些评论可能仍然提到“技术A”。只需忽略它们并享受功能完整的代码片段。)。

答案 1 :(得分:0)

水平拉伸的图像怎么样?

或者,如果您知道如何在Photoshop中制作一个,则可以通过编程方式应用相同的步骤。