不管我们是通过UIView间接创建的还是通过CALayer直接创建的layer对象,最终都会调用display方法完成绘制,绘制的时机是通过runloop观察者回调统一处理的,当然我们也可以通过调用setNeedsDisplay以及displayIfNeeded主动绘制。
dislay方法大概是这么调用的:
- (void)display {
// 如果delegate实现了displayLayer:就调用displayLayer:方法
if ([self.delegate respondsToSelector:@selector(displayLayer:)]) {
[self.delegate displayLayer:self];
} else {
// 没实现displayLayer:就会创建一个context(backing store),并且调用drawInContext:方法
// 这也就能理解为啥实现了displayLayer:方法后就不会调用drawLayer:inContext:方法了
CGContextRef ctx = CGContextCreate(); // 这个CGContextCreate方法是我写的伪代码
[self drawInContext:ctx];
}
}
附上苹果该方法部分说明原文:
If the layer has a delegate object, this method attempts to call the delegate’s displayLayer: method, which the delegate can use to update the layer’s contents. If the delegate does not implement the displayLayer: method, this method creates a backing store and calls the layer’s drawInContext: method to fill that backing store with content.
接下来看看drawInContext:方法
- (void)drawInContext:(CGContextRef)ctx {
// 调用delegate的drawLayer:inContext:方法
if ([self.delegate respondsToSelector:@selector(drawLayer:inContext:)]) {
[self.delegate drawLayer:self inContext:ctx];
}
}
附上苹果该方法部分说明原文:
The default implementation of this method does not do any drawing itself. If the layer’s delegate implements the drawLayer:inContext: method, that method is called to do the actual drawing
我们再来看看CALayerDelegate的两个方法:
-
displayLayer:方法
- (void)displayLayer:(CALayer *)layer {
// 我们可以在这里实现异步绘制
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
CGSize size = CGSizeMake(100, 100);
UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
[UIColor.redColor setFill];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];
[path fill];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
layer.contents = (__bridge id)img.CGImage;
});
});
}
-
drawLayer:inContext:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
// 如果没实现displayLayer:方法,我们也可以在这里绘制,只是不能异步绘制了
// 在这里不需要自己生成context了,直接用传过来的ctx绘制
CGContextSetFillColorWithColor(ctx, UIColor.redColor.CGColor);
CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100));
CGContextClosePath(ctx);
CGContextFillPath(ctx);
}