使用 CAGradientLayer 作为背景,同时使用 drawRect 在上下文中绘制:

Posted

技术标签:

【中文标题】使用 CAGradientLayer 作为背景,同时使用 drawRect 在上下文中绘制:【英文标题】:Using CAGradientLayer as backround while also drawing in context with drawRect: 【发布时间】:2010-07-26 11:59:13 【问题描述】:

我正在编写一个应用程序,它将一些数据绘制成一个简单的图表,有时想在后台绘制比例尺。为此,我有一个 UIView 子类,它充当图形背景并简单地使用 drawRect: 方法来绘制比例(数据元素将作为子视图添加到该视图中,因为我希望用户能够与它们交互) .

但是,我还想要渐变背景颜色,并为此使用了 CAGradientLayer(如 this thread 中所建议的那样)。但是当我将它添加为子图层时,会出现渐变背景,但我在 drawRect: 中没有做任何事情!

我确定我遗漏了一些简单的东西,或者误解了如何使用 CAGradientLayer 或其他东西,因此感谢您提供任何帮助或建议!

这是我的 UIView 子类中的相关代码:

- (id)initWithFrame:(CGRect)frame 

    if (self = [super initWithFrame:frame]) 

         // Create a gradient background
        CAGradientLayer *gradientBackground = [CAGradientLayer layer];
        gradientBackground.frame = self.bounds;
        gradientBackground.colors = [NSArray arrayWithObjects:(id)[[UIColor grayColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
        [self.layer insertSublayer:gradientBackground atIndex:0];
    

    return self;


- (void)drawRect:(CGRect)rect 

    // Get the graphics context
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Clear the context
    CGContextClearRect(context, rect);

    // Call actual draw method 
    [self drawInContext:context];


-(void)drawInContext:(CGContextRef)context 

CGFloat step;

// Draw Y scale
if (displayYScale) 

    CGContextSetRGBStrokeColor(context, 0, 0, 0, kScaleLineAlpha);

    if (yAxisScale.mode == FCGraphScaleModeData) 

        step = (self.frame.size.height-(yAxisScale.padding*2))/yAxisScale.dataRange.maximum;
        for (int i = 0; i <= yAxisScale.dataRange.maximum; i++) 

            CGContextMoveToPoint(context, 0.0f, (step*i)+yAxisScale.padding);
            CGContextAddLineToPoint(context, self.frame.size.width, (step*i)+yAxisScale.padding);
        

     else if (yAxisScale.mode == FCGraphScaleModeDates) 

        int units = (int)[yAxisScale units];
        step = (self.frame.size.height-(yAxisScale.padding*2))/units;
        for (int i = 0; i <= units; i++) 

            CGContextMoveToPoint(context, 0.0f, (step*i)+yAxisScale.padding);
            CGContextAddLineToPoint(context, self.frame.size.width, (step*i)+yAxisScale.padding);
        
    

    CGContextStrokePath(context);


// Draw X scale
if (displayXScale) 

    CGContextSetRGBStrokeColor(context, 0, 0, 255, kScaleLineAlpha);

    if (xAxisScale.mode == FCGraphScaleModeData) 

        step = (self.frame.size.width-(xAxisScale.padding*2))/xAxisScale.dataRange.maximum;
        for (int i = 0; i <= xAxisScale.dataRange.maximum; i++) 

            CGContextMoveToPoint(context, (step*i)+xAxisScale.padding, 0.0f);
            CGContextAddLineToPoint(context, (step*i)+xAxisScale.padding, self.frame.size.height);
        

     else if (xAxisScale.mode == FCGraphScaleModeDates) 

        int units = (int)[xAxisScale units];
        step = (self.frame.size.width-(xAxisScale.padding*2))/units;
        for (int i = 0; i <= units; i++) 

            CGContextMoveToPoint(context, (step*i)+xAxisScale.padding, 0.0f);
            CGContextAddLineToPoint(context, (step*i)+xAxisScale.padding, self.frame.size.height);
        
    

    CGContextStrokePath(context);


谢谢!

/安德斯

【问题讨论】:

【参考方案1】:

既然你在画,你也可以只画你自己的渐变:

// the colors
CGColorRef topColor = [[UIColor grayColor] CGColor];
CGColorRef bottomColor = [[UIColor whiteColor] CGColor];
NSArray *colors =
    [NSArray arrayWithObjects: (id)topColor, (id)bottomColor, nil];
CGFloat locations[] = 0, 1;

CGGradientRef gradient =
    CGGradientCreateWithColors(CGColorGetColorSpace(topColor),
                               (CFArrayRef)colors, locations);

// the start/end points
CGRect bounds = self.bounds;
CGPoint top = CGPointMake(CGRectGetMidX(bounds), bounds.origin.y);
CGPoint bottom = CGPointMake(CGRectGetMidX(bounds), CGRectGetMaxY(bounds));

// draw
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawLinearGradient(context, gradient, top, bottom, 0);

CGGradientRelease(gradient);

【讨论】:

当然,这也可以,谢谢!不过,我更喜欢使用 CAGradientLayer 的简单性,所以我先尝试该解决方案。【参考方案2】:

子图层将显示在 drawRect 中所做的任何事情之上。在渐变层之上添加另一个子层,使用您的视图作为委托,在该层中 drawLayer:inContext: 回调执行您当前在 drawRect 中所做的事情。

【讨论】:

啊,当然,就这么简单! :-) 与自己绘制渐变相比,我更喜欢使用 CAGradientLayer 并添加另一个图层来绘制比例的简单性(尽管这也可以完成工作)。但是,当我添加另一个层 (CALayer) 并将我的视图作为委托时,应用程序崩溃并出现一条以结尾的跟踪(请参阅下一条评论):#43651 0x002cb372 in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]当我将视图(进行绘图)作为子视图添加到滚动视图(发生在 createAndDisplayGraph 中:)时,就会发生这种情况。有什么想法吗? 我在上面的评论中提到的更多跟踪:#43651 0x002cb372 in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] #43652 0x002c2844 in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject :] #43653 0x002c9197 in -[UIView(Internal) _addSubview:positioned:relativeTo:] #43654 0x002c2e37 in -[UIView(Hierarchy) addSubview:] #43655 0x00003c9b in -[GraphScrollView createAndDisplayGraph] at GraphScrollView.m:137 哎呀,对不起,我说错了。视图不能是层的委托(除了它的根层)。要么继承 CALayer 并在内部进行绘制,要么将某些控制器对象作为其委托 我明白了,谢谢!只是出于好奇:我将尝试在视图控制器中设置图层并稍后将其设置为委托(现在没有时间),但刚刚使用 NSObject 子类作为委托进行了快速测试,并遇到了 drawLayer 的问题:inContext: 那里从来没有被调用过。是否有一个原因? (我用谷歌搜索了它,但没有任何建议与 setNeedsDisplay 和 needsDisplayOnBoundsChange = YES 有任何区别。)

以上是关于使用 CAGradientLayer 作为背景,同时使用 drawRect 在上下文中绘制:的主要内容,如果未能解决你的问题,请参考以下文章

禁用按钮时更改或删除我的 CAGradientLayer

CAGradientLayer 设置为场景包背景块场景

iOS UITableView:使用 CAGradientLayer 将背景颜色分配为渐变

使用 CAGradientLayer 类更改 UiView IOS 的渐变背景图层颜色

如何将 CAGradientLayer 应用于 UINavigationController 背景

无法在索引 0 处插入 CAGradientLayer(在 tableView 后面)