如何在Cocoa绘制锥形线+椭圆形阴影 [英] How to draw a tapered line + oval shadow in Cocoa

查看:159
本文介绍了如何在Cocoa绘制锥形线+椭圆形阴影的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:



下面的照片是OS X Lion中的Mail.app。当源列表太长时,一个漂亮的阴影线出现在源列表底部按钮的上方。滚动时,源列表在阴影线下移动。当您展开窗口,使源列表中的所有内容都不滚动时,阴影线会消失。



问题:



如何使用Cocoa绘制这条阴影线?我知道NSShadow等等,但在我看来,这里有更多的只是一个阴影。有一条线略微褪色到点(如果你在Photoshop中的每一端都应用了一个渐变蒙版)。同样,阴影是椭圆,当你接近线的末端时会逐渐变小。所以它不只是一个常规的NSShadow,是吗? (它绝对不是一个图像,因为它扩展了宽度的源视图很好地缩放。)



任何提示,如何处理绘制这种形状将非常感谢。



>



并且对于sticklers在那里,没有,这不违反NDA,因为Mail.app已被苹果公开显示。

解决方案

一般建议:




  1. 使用尺寸 150px×10px 创建一个图层A图层

    ,并用渐变


    • 较低的颜色#535e71 opacity: 33%

    • >#535e71 opacity: 0%


  2. 使用尺寸 150px×1px 创建一个Layer B图层

    固体#535e71 opacity: 50%


  3. 应用反射梯度蒙版,将组合图层A和图层B一起 #ffffff #000000 到图层C。



视觉效果:





功能代码:



.h

  #import< Cocoa / Cocoa.h> 

@interface MyView:NSView {
@private

}

@end



MyView.m

  #importMyView.h

@implementation MyView

- (CGImageRef)maskForRect:(NSRect)dirtyRect {
NSSize size = [self bounds ]。尺寸;
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,//开始颜色
0.0,0.0,0.0,1.0,//中间颜色
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);

返回掩码;
}

- (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,//底色
0.315,0.371,0.450,0.0 //顶部颜色
};

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



这是上面代码生成的实际屏幕截图:



绘图延伸到视图的尺寸。



(我以前有两种技巧,分别是技术A和技术B。

技术B提供了更好的结果,也更容易实现,所以我抛弃了技术A。

一些评论可能仍然指的是技术A,只是忽略它们,享受功能完整的代码snippet。)。


Background:

The shot below is of Mail.app in OS X Lion. When the source list gets too long, a nice shadowy line appears just above the buttons at the bottom of the source list. When you scroll, the source list moves under that shadowy line. When you expand the window so that everything in the source list fits without scrolling, the shadowy line disappears.

The question:

How can I draw this shadowy line using Cocoa? I'm aware of NSShadow and such, but it seems to me there's more going on here than just a shadow. There's a line that subtly fades to points (as if you applied a gradient mask to each end in Photoshop.) Likewise, the shadow is oval and tapers off as you approach the end of the lines. So it's not just a regular NSShadow, is it? (It's definitely not an image, as it scales nicely when you expand the width of the source view.)

Any tips on how to approach drawing this shape would be greatly appreciated.

And for the sticklers out there, no, this does not violate the NDA, as Mail.app has been shown publicly by Apple.

解决方案

General Idea:

.

  1. Create a layer "Layer A" with dimensions 150px × 10px
    and fill it with a Gradient with:
    • lower color: #535e71 opacity: 33%
    • upper color: #535e71 opacity: 0%
  2. Create a layer "Layer B" with dimensions 150px × 1px
    and fill it with solid #535e71 opacity: 50%
  3. Compose "Layer A" and "Layer B" together into "Layer C".
  4. Apply reflected gradient mask from #ffffff to #000000 to "Layer C".

Visual Steps:

Functional Code:

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

(My first time using pure low-level CoreGraphics, thus possibly sub-par optimal, open for improvements.)

This is an actual screenshot of what the code above produces:

The drawing stretches to the view's dimensions.

(I formerly had two techniques shown here: "Technique A" & "Technique B".
"Technique B" provided superior results and was way simpler to implement as well, so I ditched "Technique A".
Some comments may still refer to "Technique A" though. Just ignore them and enjoy the fully functional code snippet.).

这篇关于如何在Cocoa绘制锥形线+椭圆形阴影的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆