2010-07-01 46 views
14

EDIT:Vẽ một UIView tròn với độ dốc và thả bóng

cuối cùng tôi đã tìm thấy một giải pháp đơn giản thực cho vấn đề này, bằng cách sử dụng lớp CAGradientLayer, và các chức năng vẽ CALayer.
Ole Begemann đã phát hành một trình bao bọc UIView tuyệt vời cho lớp CAGradientLayer có tên OBGradientView.
Lớp này cho phép bạn dễ dàng tạo ra một UIView gradient trong ứng dụng của bạn.
Sau đó bạn sử dụng chức năng CALayer vẽ thêm các góc bo tròn và thả các giá trị bóng:

// Create the gradient view 
OBGradientView *gradient = [[OBGradientView alloc] initWithFrame:someRect]; 
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor yellowColor], nil]; 
gradient.colors = colors; 

// Set rounded corners and drop shadow 
gradient.layer.cornerRadius = 5.0; 
gradient.layer.shadowColor = [UIColor grayColor].CGColor; 
gradient.layer.shadowOpacity = 1.0; 
gradient.layer.shadowOffset = CGSizeMake(2.0, 2.0); 
gradient.layer.shadowRadius = 3.0; 

[self.view addSubview:gradient]; 
[gradient release]; 

Dont quên thêm khuôn khổ QuartzCore để dự án của bạn.



HỎI ORIGINAL:

Tôi đã được làm việc trên một điều khiển tùy chỉnh mà là một nút hình chữ nhật tròn, đầy với một linear gradient, và có một bóng thả. Tôi đã điền hai bước đầu tiên bằng cách sử dụng câu trả lời này: link text

Vấn đề của tôi bây giờ là thêm bóng thả dưới hình dạng kết quả. Thực ra, ngữ cảnh đã được cắt bớt thành đường tròn được làm tròn, vì vậy khi tôi sử dụng hàm CGContextSetShadow, nó không vẽ nó.

Tôi đã cố gắng giải quyết vấn đề này bằng cách vẽ bản lề tròn hai lần, đầu tiên với màu đồng bằng, do đó, nó vẽ bóng và sau đó vẽ lại bằng tô màu tô.

Nó kinda làm việc, nhưng tôi vẫn có thể thấy một vài điểm ảnh ở các góc của hình phát sinh từ việc bốc thăm đầu tiên với một màu đơn giản, như bạn có thể nhìn thấy trên phiên bản này zoom:

http://img269.imageshack.us/img269/6489/capturedcran20100701192.png

nó gần như là tốt, nhưng chưa hoàn hảo ...

đây là -drawRect tôi: thực hiện:

static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight) 
{ 
float fw, fh; 

if (ovalWidth == 0 || ovalHeight == 0) { 
    CGContextAddRect(context, rect); 
    return; 
} 
CGContextSaveGState(context); 
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect)); 
CGContextScaleCTM (context, ovalWidth, ovalHeight); 
fw = CGRectGetWidth (rect)/ovalWidth; 
fh = CGRectGetHeight (rect)/ovalHeight; 
CGContextMoveToPoint(context, fw, fh/2); 
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1); 
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1); 
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1); 
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); 
CGContextClosePath(context); 
CGContextRestoreGState(context); 
} 


- (void)drawRect:(CGRect)rect 
{ 
CGContextRef context = UIGraphicsGetCurrentContext(); 

CGSize shadowOffset = CGSizeMake(10.0, 10.0); 
CGFloat blur = 5.0; 

rect.size.width -= shadowOffset.width + blur; 
rect.size.height -= shadowOffset.height + blur; 

CGContextSaveGState(context); 
addRoundedRectToPath(context, rect, _radius, _radius); 
CGContextSetShadow (context, shadowOffset, blur); 
CGContextDrawPath(context, kCGPathFill); 
CGContextRestoreGState(context); 

addRoundedRectToPath(context, rect, _radius, _radius); 
    CGContextClip(context); 

CGFloat colors[] = 
{ 
    _gradientStartColor.red, _gradientStartColor.green, _gradientStartColor.blue, _gradientStartColor.alpha, 
    _gradientEndColor.red, _gradientEndColor.green, _gradientEndColor.blue, _gradientEndColor.alpha 
}; 
size_t num_locations = 2; 
    CGFloat locations[2] = { 0.0, 1.0 }; 

CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); 
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, locations, num_locations); 

CGRect currentBounds = self.bounds; 
CGPoint gStartPoint = CGPointMake(CGRectGetMidX(currentBounds), 0.0f); 
CGPoint gEndPoint = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMaxY(currentBounds)); 
CGContextDrawLinearGradient(context, gradient, gStartPoint, gEndPoint, 0); 

CGColorSpaceRelease(rgb); 
CGGradientRelease(gradient); 
} 

Bất kỳ ý tưởng về cách làm điều này theo một cách khác?

Cảm ơn!

Trả lời

24

Để tạo một cái nhìn góc tròn với một nền gradient và thả bóng, đây là những gì đã làm:

Phần đầu tiên rất giống với những gì được cung cấp trong câu hỏi, nó tạo ra một đường tròn được làm tròn bằng CGPathAddArcToPoint như mô tả rất tốt trong this article.Dưới đây là một bức tranh để giúp tôi hiểu nó: alt text

Phần thứ hai hoạt động như sau:

Enable shadowing vào bối cảnh đồ họa, thêm đường dẫn vừa được xác định, sau đó điền vào con đường đó. Bạn không thể áp dụng bóng cho chính con đường (đường dẫn không phải là một phần của trạng thái đồ họa), vì vậy bạn cần phải điền vào đường dẫn để cho bóng xuất hiện (tôi cho rằng một con đường vuốt ve cũng có thể hoạt động?). Bạn không thể chỉ đơn giản là áp dụng bóng cho một gradient vì nó không thực sự là một điền tiêu chuẩn (xem this post để biết thêm thông tin).

Khi bạn đã tạo hình tròn được làm đầy tạo bóng, bạn cần vẽ gradient lên trên đỉnh đó. Vì vậy, thêm đường dẫn lần thứ hai để thiết lập vùng cắt, sau đó vẽ gradient bằng CGContextDrawLinearGradient. Tôi không nghĩ rằng bạn có thể dễ dàng "điền" một đường dẫn với độ dốc giống như bạn có thể làm với bước điền chuẩn trước đó, vì vậy thay vào đó bạn điền vào vùng vẽ bằng gradient và sau đó clip vào vùng hình chữ nhật tròn mà bạn quan tâm in.

- (void)drawRect:(CGRect)rect 
{ 
    [super drawRect:rect]; 

    CGGradientRef gradient = [self normalGradient]; 

    CGContextRef ctx = UIGraphicsGetCurrentContext(); 
    CGMutablePathRef outlinePath = CGPathCreateMutable(); 
    float offset = 5.0; 
    float w = [self bounds].size.width; 
    float h = [self bounds].size.height; 
    CGPathMoveToPoint(outlinePath, nil, offset*2.0, offset); 
    CGPathAddArcToPoint(outlinePath, nil, offset, offset, offset, offset*2, offset); 
    CGPathAddLineToPoint(outlinePath, nil, offset, h - offset*2.0); 
    CGPathAddArcToPoint(outlinePath, nil, offset, h - offset, offset *2.0, h-offset, offset); 
    CGPathAddLineToPoint(outlinePath, nil, w - offset *2.0, h - offset); 
    CGPathAddArcToPoint(outlinePath, nil, w - offset, h - offset, w - offset, h - offset * 2.0, offset); 
    CGPathAddLineToPoint(outlinePath, nil, w - offset, offset*2.0); 
    CGPathAddArcToPoint(outlinePath, nil, w - offset , offset, w - offset*2.0, offset, offset); 
    CGPathCloseSubpath(outlinePath); 

    CGContextSetShadow(ctx, CGSizeMake(4,4), 3); 
    CGContextAddPath(ctx, outlinePath); 
    CGContextFillPath(ctx); 

    CGContextAddPath(ctx, outlinePath); 
    CGContextClip(ctx); 
    CGPoint start = CGPointMake(rect.origin.x, rect.origin.y); 
    CGPoint end = CGPointMake(rect.origin.x, rect.size.height); 
    CGContextDrawLinearGradient(ctx, gradient, start, end, 0); 

    CGPathRelease(outlinePath); 
} 

- (CGGradientRef)normalGradient 
{ 

    NSMutableArray *normalGradientLocations = [NSMutableArray arrayWithObjects: 
               [NSNumber numberWithFloat:0.0f], 
               [NSNumber numberWithFloat:1.0f], 
               nil]; 


    NSMutableArray *colors = [NSMutableArray arrayWithCapacity:2]; 

    UIColor *color = [UIColor colorWithRed:0.2745 green:0.2745 blue:0.2745 alpha:1.0]; 
    [colors addObject:(id)[color CGColor]]; 
    color = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1.0]; 
    [colors addObject:(id)[color CGColor]]; 
    NSMutableArray *normalGradientColors = colors; 

    int locCount = [normalGradientLocations count]; 
    CGFloat locations[locCount]; 
    for (int i = 0; i < [normalGradientLocations count]; i++) 
    { 
     NSNumber *location = [normalGradientLocations objectAtIndex:i]; 
     locations[i] = [location floatValue]; 
    } 
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); 

    CGGradientRef normalGradient = CGGradientCreateWithColors(space, (CFArrayRef)normalGradientColors, locations); 
    CGColorSpaceRelease(space); 

    return normalGradient; 
} 
+0

Cảm ơn bạn vì lời giải thích rất chi tiết này! Ví dụ của bạn hoạt động hoàn hảo. Tôi chỉ cần sửa CGGradientRef của bạn từ CGGradientRef * normalGradient thành CGGradientRef normalGradient. –

+0

Bạn có thể giải thích cách các đường tiếp tuyến và các điểm được tạo ra từ các đối số không? Tôi không hiểu làm thế nào bạn có thể tạo ra hai dòng và hai điểm từ 4 đối số ... – ryyst

+0

@super_tomtom ai đó có thể giải thích cách hoạt động này? Tôi đã subclasses một UIView tùy chỉnh và thực hiện chức năng này nó ghi đè drawRect nhưng tôi vẫn nhận được không có góc tròn hoặc bóng đổ? –

1

Đối với bóng tối, bạn có thể sử dụng CGContextSetShadow()

Mã này sẽ vẽ một cái gì đó với một cái bóng:

- (void)drawTheRealThingInContext:(CGContextRef)ctx 
{ 
     // calculate x, y, w, h and inset here... 

    CGContextMoveToPoint(ctx, x+inset, y); 
    CGContextAddLineToPoint(ctx, x+w-inset, y); 
    CGContextAddArcToPoint(ctx, x+w, y, x+w, y+inset, inset); 
    CGContextAddLineToPoint(ctx, x+w, y+w-inset); 
    CGContextAddArcToPoint(ctx,x+w, y+w, x+w-inset, y+w, inset); 
    CGContextAddLineToPoint(ctx, x+inset, y+w); 
    CGContextAddArcToPoint(ctx,x, y+w, x, y+w-inset, inset); 
    CGContextAddLineToPoint(ctx, x, y+inset); 
    CGContextAddArcToPoint(ctx,x, y, x+inset, y, inset);  
} 
- (void)drawRect:(CGRect)rect { 

    CGContextRef ctx = UIGraphicsGetCurrentContext(); 
    CGFloat color[4];color[0] = 1.0;color[1] = 1.0;color[2] = 1.0;color[3] = 1.0; 
    CGFloat scolor[4];scolor[0] = 0.4;scolor[1] = 0.4;scolor[2] = 0.4;scolor[3] = 0.8; 

    CGContextSetFillColor(ctx, color); 

    CGContextSaveGState(ctx); 
    CGSize myShadowOffset = CGSizeMake (3, -3); 
    CGContextSetShadow (ctx, myShadowOffset, 1); 

    CGContextBeginPath(ctx); 

    [self drawTheRealThingInContext:ctx]; 

    CGContextFillPath(ctx); 
    CGContextRestoreGState(ctx); 
} 
+0

Có, điều này chắc chắn hoạt động, nhưng vấn đề của tôi là tôi muốn điền vào hình dạng với một gradient. Để làm như vậy, tôi cần sử dụng chức năng CGContextClip. Và một khi bối cảnh bị cắt bớt, nó dường như không vẽ bóng nữa. –

2

Tôi có giải pháp không cần điền trước đường dẫn. Lợi thế (?) Là bóng có thể sử dụng hiệu ứng trong suốt của gradient (tức là nếu gradient từ mờ đến trasparent, bóng sẽ được một phần trong suốt là tốt) và đơn giản hơn.

Nó đi nhiều hơn hoặc ít hơn như:

CGContextSetShadowWithColor(); 
CGContextBeginTransparencyLayer(); 

CGContextSaveGState(); 
CGContextClip(); 
CGGradientCreateWithColorComponents(); 
CGContextRestoreGState(); 

CGContextEndTransparencyLayer(); 
CGContextSetShadowWithColor(..., NULL); 

Tôi cho rằng đó là beacuse CGContextBeginTransparencyLayer/CGContextEndTransparencyLayer nằm ngoài các clip và cái bóng được áp dụng cho lớp đó (trong đó có độ dốc điền đường dẫn). Ít nhất nó dường như làm việc cho tôi.

1

Vấn đề (ban đầu) của bạn là bạn lại vẽ bóng khi vẽ gradient. Bóng này có độ lệch (0,0) và một chút mờ, chỉ chiếu qua các góc. Trong dòng trước khi CGContextDrawLinearGradient (…), thêm như sau:

CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 0, NULL); 

Giá trị màu NULL vô hiệu hóa bóng và sẽ loại bỏ hiệu ứng góc.

Các vấn đề liên quan