如何将 CALayer 子类化为另一个 CALayer 掩码?

Posted

技术标签:

【中文标题】如何将 CALayer 子类化为另一个 CALayer 掩码?【英文标题】:How to subclass CALayer for use as another CALayer mask? 【发布时间】:2014-07-13 15:33:23 【问题描述】:

我正在尝试继承 CALayer 以用作另一层的蒙版。

我想使用我的CALayer 子类代替CAGradientLayer,并将其用于渐变蒙版,如here 所述。

但是,我希望使用自定义CALayer 来代替使用内部CGGradient 进行绘图,因为这应该会产生比CAGradientLayer (see here) 更平滑的结果。我不关心性能,我想要更好的渐变质量。

我正在关注 this example 创建我的 CGGradient 并将其存储在 CALayer.. 但是,我无法绘制蒙版。

我不知道把draw code放在哪里:CALayerdisplay也不是drawInContext:(CGContextRef)ctx也不是drawInContext:(CGContextRef)ctx在用作掩码时似乎被调用了。

请耐心等待,因为我是 CoreAnimation 的新手。那么,如何解决这个问题,以便我的CALayer 子类可以替代CAGradientLayer,但使用CGGradient 进行绘制?


我当前的代码在这里:

@interface CANiceGradientLayer : CALayer

@property (nonatomic) CGGradientRef gradient;
@property (atomic) CGPoint startPoint;
@property (atomic) CGPoint endPoint;

@end

@implementation CANiceGradientLayer

- (instancetype)initWithGradientRef:(CGGradientRef)gradient startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint

    if ( !(self = [super init]) )
    
        return nil;
    

    self.gradient = CGGradientRetain(gradient);
    self.startPoint = startPoint;
    self.endPoint = endPoint;

    return self;


- (void)dealloc

    CGGradientRelease(self.gradient);


- (void)display

    NSLog(@"display");


- (void)drawInContext:(CGContextRef)ctx

    NSLog(@"drawInContext:");
    CGContextDrawLinearGradient(ctx, self.gradient, self.startPoint, self.endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);;


- (void)renderInContext:(CGContextRef)ctx

    NSLog(@"renderInContext:");


@end

这就是我的创建方式:

size_t num_locations = 2;
CGFloat locations[2] =  0.0, 1.0 ;
CGFloat components[8] =  1.0, 1.0, 1.0, 1.0,  // Start color
                          1.0, 1.0, 1.0, 0.0 ; // End color

CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);


self.collectionViewTickerMaskLayer = [[CANiceGradientLayer alloc] initWithGradientRef:gradient startPoint:CGPointZero endPoint:CGPointZero];


self.collectionViewTickerMaskLayer.anchorPoint = CGPointZero;
view.layer.mask = self.collectionViewTickerMaskLayer;

如果我改用CAGradientLayer,它可以正常工作(但渐变看起来很糟糕)。

【问题讨论】:

只是猜测:继承自 CAShapeLayer 而不是 CALayer ? - 我认为您必须将 CAShapeLayers 用于 mask 属性(尽管它接受任何 CALayer 对象的事件)... @Cabus OP 说使用 CAGradientLayer 也可以,所以我认为这不是问题。 我看到你正在覆盖renderInContext:。这可能是问题吗?请尝试调用超级实现或将其注释掉,看看是否有变化。 【参考方案1】:

在您的图层子类上调用-setNeedsDisplay,然后再将其设置为遮罩,您只需覆盖CALayer 子类的-drawInContext:。这将帮助您获得要调用的方法(CALayer 调用-drawInContext: 使用其默认实现-display,在-setNeedsDisplay 之后调用)。您可能还需要设置图层的框架:

self.collectionViewTickerMaskLayer.frame = view.layer.bounds;
self.collectionViewTickerMaskLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;

此代码存在逻辑问题。您使用的是 CGPointZero 端点,因此即使调用了这些方法,也不会绘制渐变来遮盖您的图层。

【讨论】:

就是这样,我错过了-setNeedsDisplay。添加后drawInContext: 被称为老板。

以上是关于如何将 CALayer 子类化为另一个 CALayer 掩码?的主要内容,如果未能解决你的问题,请参考以下文章

AFNetworking 2.0 - 如何从子类 AFHTTPSessionManager 成功将响应传递给另一个类

ios开发讲解之anchorPoint和position详解

如何将 UIView 添加到 CALayer

自定义 CALayer 子类未显示

CALayer 的子类 VS 类别

Calayer子类问题