美文网首页
Graver初探

Graver初探

作者: Bel李玉 | 来源:发表于2020-02-07 20:33 被阅读0次

美团开源Graver框架:用“雕刻”诠释iOS端UI界面的高效渲染
Graver是一个App渲染框架,采用异步渲染的方式,很好的解决了App渲染时的性能损耗。今天我们带着两个问题:1,UIView的绘制过程。2,如何实现异步绘制成图片的。

UIView的绘制流程

UIView的绘制离不开CALayerUIView的layer是CALayer,CALayer的delegate(CALayerDelegate)是UIViewCALayer主要负责内容,当内容改变时通过CALayerDelegate代理方法来询问UIView的渲染实现。

966599-a9a7090908e3588a.png
我们看一下UIView的drawRect的函数调用栈: Snip20200207_7.png

首先会调用 display方法,该方法会默认询问CALayerDelegate的 - (void)displayLayer:(CALayer *)layer;方法,先后在调用 drawInContext:,该方法会默认询问 CALayerDelegate- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;方法,最后调用drawRect:方法。

Graver中,是通过在UIView中重写CALayerDelegate- (void)displayLayer:(CALayer *)layer;方法来接管绘制过程。

如何实现异步绘制成图片的

具体的绘制过程是通过CoreGraphicsCoreText进行绘制的,UIView的 背景色,背景图片,边框,圆角 使用 CoreGraphics实现,文字图片信息主要使用CoreText进行绘制。
通过Graver我们看下主要的绘制过程,新建LYDrawView继承于UIView

-(void)displayLayer:(LYDrawLayer *)layer {
    if (!layer) {
        return;
    }
    [self _displayLayer:layer rect:self.bounds];

}

- (BOOL)drawInRect:(CGRect)rect withContext:(CGContextRef)context asynchronously:(BOOL)asynchronously userInfo:(NSDictionary *)userInfo {

    // backgroundColor
    CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
    CGContextFillRect(context, rect);

    // borderWidth
    CGContextAddPath(context, [UIBezierPath bezierPathWithRect:rect].CGPath);
    CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    CGContextSetLineWidth(context, 0.5);
    CGContextDrawPath(context, kCGPathFillStroke);

    UIGraphicsPushContext(context);
    UIImage *image = [UIImage imageNamed:@"a5a61ab8c196836fe1efbcd9d33edc44"];
    [image drawInRect:CGRectInset(rect, 8, 8)];
    UIGraphicsPopContext();

    return YES;
}

- (void) _displayLayer:(LYDrawLayer *)layer rect:(CGRect) rectToDraw {
    BOOL drawInBackground = layer.isAsyncDrawsCurrentContent ;

    // 绘制Block
    void(^drawBlock)(void) = ^{
        CGSize contextSize = layer.bounds.size;
        BOOL contextSizeValid = contextSize.width >= 1 && contextSize.height >= 1;
        CGContextRef context = NULL;
        BOOL drawingFinished = YES;

        if (contextSizeValid) {
            
            UIGraphicsBeginImageContextWithOptions(contextSize, layer.isOpaque, layer.contentsScale);

            context = UIGraphicsGetCurrentContext();

            CGContextSaveGState(context);

            if (rectToDraw.origin.x || rectToDraw.origin.y) {
                CGContextTranslateCTM(context, rectToDraw.origin.x, -rectToDraw.origin.y);
            }

            drawingFinished = [self drawInRect:rectToDraw withContext:context asynchronously:drawInBackground userInfo:@{}];

            CGContextRestoreGState(context);
        }

        if (drawingFinished) {
            CGImageRef CGImage = context ? CGBitmapContextCreateImage(context) : NULL;
            {
                UIImage *image = CGImage ? [UIImage imageWithCGImage:CGImage] : nil;

                void (^finishBlock)(void) = ^{
                    layer.contents = (id)image.CGImage;
                };

                if (drawInBackground) {
                    dispatch_async(dispatch_get_main_queue(), finishBlock);
                }else {
                    finishBlock();
                }
            }
            if (CGImage) {
                CGImageRelease(CGImage);
            }
        }
        UIGraphicsEndImageContext();
    };


    if (drawInBackground) {

        layer.contents = nil;

        dispatch_async(dispatch_get_global_queue(0, 0),drawBlock);
    }
}

新建 LYDrawTextView继承于 LYDrawView,重写- (BOOL)drawInRect:(CGRect)rect withContext:(CGContextRef)context asynchronously:(BOOL)asynchronously userInfo:(NSDictionary *)userInfo方法

-(BOOL)drawInRect:(CGRect)rect withContext:(CGContextRef)context asynchronously:(BOOL)asynchronously userInfo:(NSDictionary *)userInfo {
    [super drawInRect:rect withContext:context asynchronously:asynchronously userInfo:userInfo];

    CGContextTranslateCTM(context, 0, rect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    // 创建 绘制的区域
    CGMutablePathRef path = CGPathCreateMutable();
       CGRect bounds = CGRectInset(rect, 8, 8);
       CGPathAddRect(path, NULL, bounds);

    // 创建NSMutableString
       CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine.");

       CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
       // 将 textString 复制到 attrString中
       CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), textString);

       // 创建Color
       CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
       CGFloat components[] = {1.0,0.0,0.0,0.8};
       CGColorRef red = CGColorCreate(rgbColorSpace, components);
       CGColorSpaceRelease(rgbColorSpace);

       // 设置前12位的颜色
       CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 12), kCTForegroundColorAttributeName, red);

       // 使用attrString 创建 framesetter
       CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
       CFRelease(attrString);

       // 创建ctframe
       CTFrameRef ctframe = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
       // 在当前context 绘制ctframe
       CTFrameDraw(ctframe, context);

       CFRelease(ctframe);
       CFRelease(path);
       CFRelease(framesetter);

    return YES;
}

最终的效果图:


图.png

Graver中 通过-(BOOL)drawInRect:(CGRect)rect withContext:(CGContextRef)context asynchronously:(BOOL)asynchronously userInfo:(NSDictionary *)userInfo方法向context绘制元素,在所有子类的绘制任务完成时,将context合成一张图片,赋值到layer.contents中。

示例代码

相关文章

网友评论

      本文标题:Graver初探

      本文链接:https://www.haomeiwen.com/subject/kphvxhtx.html