iOS:为啥 UIView 的 drawRect 比 CALayer 的 drawInContext 性能好

Posted

技术标签:

【中文标题】iOS:为啥 UIView 的 drawRect 比 CALayer 的 drawInContext 性能好【英文标题】:iOS: Why UIView's drawRect has better performance than CALayer's drawInContextiOS:为什么 UIView 的 drawRect 比 CALayer 的 drawInContext 性能好 【发布时间】:2015-12-01 11:54:05 【问题描述】:

我目前正在学习使用 UIView 和 CALayer 进行绘图。我很快做了一个绘图应用程序来试验这两个类。我注意到 CALayer 的性能比 UIView 差。代码如下:

myView.m

@interface myView()

@property (nonatomic, strong) UIImageView *background;

#ifdef USE_LAYER
@property (nonatomic, strong) drawingLayer *drawCanvas;
#else
@property (nonatomic, strong) drawingView  *drawCanvas;
#endif
@end

@implementation myView

- (instancetype)initWithFrame:(CGRect)frame

    if (self = [super initWithFrame:frame])
    
        self.background = [[UIImageView alloc] initWithFrame:frame];
        [self addSubview:self.background];
#ifdef USE_LAYER
        self.drawCanvas = [[drawingLayer alloc] init];
        self.drawCanvas.frame = frame;
        [self.layer addSublayer:self.drawCanvas];
#else
        self.drawCanvas = [[drawingView alloc] initWithFrame:frame];
        self.drawCanvas.backgroundColor = [UIColor clearColor];
        [self addSubview:self.drawCanvas];
#endif
    
    return self;


- (CGPoint)getPoint:(NSSet<UITouch *> *)touches

    NSArray *touchesArray = [touches allObjects];
    UITouch *touch = (UITouch *)[touchesArray objectAtIndex:0];
    return [touch locationInView:self];


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

    CGPoint point = [self getPoint:touches];
    self.drawCanvas.points = [[NSMutableArray alloc] init];
    self.drawCanvas.startPoint = point;


- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

    CGPoint point = [self getPoint:touches];
    [self.drawCanvas.points addObject:[NSValue valueWithCGPoint:point]];
    self.background.image = [self imageFromLayer:self.layer];
    self.drawCanvas.points = nil;
    [self.drawCanvas setNeedsDisplay];


- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

    CGPoint point = [self getPoint:touches];
    [self.drawCanvas.points addObject:[NSValue valueWithCGPoint:point]];
    [self.drawCanvas setNeedsDisplay];


- (UIImage *)imageFromLayer:(CALayer *)layer

    UIGraphicsBeginImageContext([layer frame].size);
    [layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return outputImage;


@end

drawingView.h

@interface drawingView : UIView

@property (nonatomic, assign) CGPoint startPoint;
@property (nonatomic, strong) NSMutableArray *points;

@end

drawingView.m

@implementation drawingView

- (void)drawRect:(CGRect)rect 
     CGContextRef context = UIGraphicsGetCurrentContext();
     CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);

     CGContextSetLineWidth(context, 2.0f);
     CGContextMoveToPoint(context, self.startPoint.x, self.startPoint.y);

     for (NSValue *point in self.points)
     
         CGPoint location = [point CGPointValue];
         CGContextAddLineToPoint(context, location.x, location.y);
     
     CGContextStrokePath(context);


@end

我还有“drawingLayer”,它与“drawingView”本质上是一样的,除了它是 CALayer 的子类并且在“drawInContext”中进行相同的绘图。

我发现 drawRect 的性能要好得多,绘图几乎与触摸位置同步,延迟非常小。 为什么会这样?我希望 CALayer 绘图至少是相同的,如果不是更好的话。事实上,今天有人告诉我,在 CALayer 中绘图是硬件加速的,如果考虑性能,这是首选方式。当然,在我的小实验中情况并非如此。为什么?

【问题讨论】:

CALayer 动画可以让它看起来更慢。也许这就是你所观察到的 你觉得它慢了,还是真的慢了?您是否使用过 Instruments 来验证这一点?此外,您是否尝试过更新 CAShapeLayer 的 cgPath?这也可能会提高性能。 【参考方案1】:

可能是CALayer的默认动画造成的。

尝试覆盖 action(forKey event: String) -&gt; CAAction? 并在继承 CALayer 类时始终返回 nil

【讨论】:

以上是关于iOS:为啥 UIView 的 drawRect 比 CALayer 的 drawInContext 性能好的主要内容,如果未能解决你的问题,请参考以下文章

iOS重绘机制drawRect

iOS UIView drawRect 调整大小动画

iOS8 问题 - UIView drawRect 未被调用

iOS drawRect的作用和调用机制

iOS开发之drawRect的作用和调用机制

为啥尝试使用 drawRect 绘制彩色圆圈时颜色是透明的?