I am currently learning drawing with UIView and CALayer. I quickly did a drawing app to experiment with the two classes. I noticed CALayer had worse performance than UIView. Here is the code:
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
I also have "drawingLayer" which is essentially the same thing as "drawingView", except it a subclass of CALayer and does the same drawing in "drawInContext".
What I found was the drawRect has much better performance, the drawing is almost in-sync with the touch position, with very minor lag. Why is this the case? I expected the CALayer drawing to be at least the same, if not better. In fact, someone told me today that drawing in CALayer is hardware-accelerated, which is the preferred way if performance is a concern. Certainly this is NOT the case in my little experiment. Why?