美文网首页
YYAsyncLayer

YYAsyncLayer

作者: Code_人生 | 来源:发表于2019-09-30 14:34 被阅读0次
        NSMutableAttributedString *text = [NSMutableAttributedString new];
        
        {
            //Core Graphics
            NSMutableAttributedString *one = [[NSMutableAttributedString alloc] initWithString:@"Shadow"];
            one.font = [UIFont boldSystemFontOfSize:30];
            one.color = [UIColor whiteColor];
            YYTextShadow *shadow = [YYTextShadow new];
            shadow.color = [UIColor colorWithWhite:0.000 alpha:0.490];
            shadow.offset = CGSizeMake(0, 1);
            shadow.radius = 5;
            one.textShadow = shadow;
            [text appendAttributedString:one];
            [text appendAttributedString:[self padding]];
        }
    
        YYLabel *label = [YYLabel new];
        label.attributedText = text;
        label.width = self.view.width;
        label.height = self.view.height - (kiOS7Later ? 64 : 44);
        label.top = (kiOS7Later ? 64 : 0);
        label.textAlignment = NSTextAlignmentCenter;
        label.textVerticalAlignment = YYTextVerticalAlignmentCenter;
        label.numberOfLines = 0;
        label.backgroundColor = [UIColor colorWithWhite:0.933 alpha:1.000];
        [self.view addSubview:label];
    

    1、点击textShadow

    - (void)setTextShadow:(YYTextShadow *)textShadow {
        [self setTextShadow:textShadow range:NSMakeRange(0, self.length)];
    }
    
    - (void)setTextShadow:(YYTextShadow *)textShadow range:(NSRange)range {
        //key-value的形式
        [self setAttribute:YYTextShadowAttributeName value:textShadow range:range];
    }
    
    - (void)setAttribute:(NSString *)name value:(id)value range:(NSRange)range {
        if (!name || [NSNull isEqual:name]) return;
        if (value && ![NSNull isEqual:value]) [self addAttribute:name value:value range:range];
        else [self removeAttribute:name range:range];
    }
    

    2、搜索YYTextShadowAttributeName

    //阴影绘制, Core Text : Graver
    static void YYTextDrawShadow(YYTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) {
        //move out of context. (0xFFFF is just a random large number)
        CGFloat offsetAlterX = size.width + 0xFFFF;
        BOOL isVertical = layout.container.verticalForm;
        CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0;
        
        CGContextSaveGState(context);{
            CGContextTranslateCTM(context, point.x, point.y);
            CGContextTranslateCTM(context, 0, size.height);
            CGContextScaleCTM(context, 1, -1);
            NSArray *lines = layout.lines;
            for (NSUInteger l = 0, lMax = layout.lines.count; l < lMax; l++) {
                if (cancel && cancel()) break;
                YYTextLine *line = lines[l];
                if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine;
                NSArray *lineRunRanges = line.verticalRotateRange;
                CGFloat linePosX = line.position.x;
                CGFloat linePosY = size.height - line.position.y;
                CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine);
                for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) {
                    CTRunRef run = CFArrayGetValueAtIndex(runs, r);
    //                CGContextSetTextMatrix(context, CGAffineTransformIdentity);
                    CGContextSetTextPosition(context, linePosX, linePosY);
                    NSDictionary *attrs = (id)CTRunGetAttributes(run);
                    YYTextShadow *shadow = attrs[YYTextShadowAttributeName];
                    YYTextShadow *nsShadow = [YYTextShadow shadowWithNSShadow:attrs[NSShadowAttributeName]]; // NSShadow compatible
                    if (nsShadow) {
                        nsShadow.subShadow = shadow;
                        shadow = nsShadow;
                    }
                    while (shadow) {
                        if (!shadow.color) {
                            shadow = shadow.subShadow;
                            continue;
                        }
                        CGSize offset = shadow.offset;
                        offset.width -= offsetAlterX;
                        CGContextSaveGState(context); {
                            CGContextSetShadowWithColor(context, offset, shadow.radius, shadow.color.CGColor);
                            CGContextSetBlendMode(context, shadow.blendMode);
                            CGContextTranslateCTM(context, offsetAlterX, 0);
                            YYTextDrawRun(line, run, context, size, isVertical, lineRunRanges[r], verticalOffset);
                        } CGContextRestoreGState(context);
                        shadow = shadow.subShadow;
                    }
                }
            }
        }CGContextRestoreGState(context);
    }
    

    2.1、找到谁调用了YYTextDrawShadow方法

    - (void)drawInContext:(CGContextRef)context
                     size:(CGSize)size
                    point:(CGPoint)point
                     view:(UIView *)view
                    layer:(CALayer *)layer
                    debug:(YYTextDebugOption *)debug
                    cancel:(BOOL (^)(void))cancel{
        @autoreleasepool {
            if (self.needDrawBlockBorder && context) {
                if (cancel && cancel()) return;
                YYTextDrawBlockBorder(self, context, size, point, cancel);
            }
            if (self.needDrawBackgroundBorder && context) {
                if (cancel && cancel()) return;
                YYTextDrawBorder(self, context, size, point, YYTextBorderTypeBackgound, cancel);
            }
            if (self.needDrawShadow && context) {
                if (cancel && cancel()) return;
                YYTextDrawShadow(self, context, size, point, cancel);
            }
            if (self.needDrawUnderline && context) {
                if (cancel && cancel()) return;
                YYTextDrawDecoration(self, context, size, point, YYTextDecorationTypeUnderline, cancel);
            }
            if (self.needDrawText && context) {
                if (cancel && cancel()) return;
                YYTextDrawText(self, context, size, point, cancel);
            }
            if (self.needDrawAttachment && (context || view || layer)) {
                if (cancel && cancel()) return;
                YYTextDrawAttachment(self, context, size, point, view, layer, cancel);
            }
            if (self.needDrawInnerShadow && context) {
                if (cancel && cancel()) return;
                YYTextDrawInnerShadow(self, context, size, point, cancel);
            }
            if (self.needDrawStrikethrough && context) {
                if (cancel && cancel()) return;
                YYTextDrawDecoration(self, context, size, point, YYTextDecorationTypeStrikethrough, cancel);
            }
            if (self.needDrawBorder && context) {
                if (cancel && cancel()) return;
                YYTextDrawBorder(self, context, size, point, YYTextBorderTypeNormal, cancel);
            }
            if (debug.needDrawDebug && context) {
                if (cancel && cancel()) return;
                YYTextDrawDebug(self, context, size, point, debug);
            }
        }
    }
    
    • callers [YYLabel newAsyncDisplayTask]
    - (YYAsyncLayerDisplayTask *)newAsyncDisplayTask {
        
        // capture current context
        BOOL contentsNeedFade = _state.contentsNeedFade;
        NSAttributedString *text = _innerText;
        YYTextContainer *container = _innerContainer;
        YYTextVerticalAlignment verticalAlignment = _textVerticalAlignment;
        YYTextDebugOption *debug = _debugOption;
        NSMutableArray *attachmentViews = _attachmentViews;
        NSMutableArray *attachmentLayers = _attachmentLayers;
        BOOL layoutNeedUpdate = _state.layoutNeedUpdate;
        BOOL fadeForAsync = _displaysAsynchronously && _fadeOnAsynchronouslyDisplay;
        __block YYTextLayout *layout = (_state.showingHighlight && _highlightLayout) ? self._highlightLayout : self._innerLayout;
        __block YYTextLayout *shrinkLayout = nil;
        __block BOOL layoutUpdated = NO;
        if (layoutNeedUpdate) {
            text = text.copy;
            container = container.copy;
        }
        
        // create display task
        YYAsyncLayerDisplayTask *task = [YYAsyncLayerDisplayTask new];
        
        task.willDisplay = ^(CALayer *layer) {
            [layer removeAnimationForKey:@"contents"];
            
            // If the attachment is not in new layout, or we don't know the new layout currently,
            // the attachment should be removed.
            for (UIView *view in attachmentViews) {
                if (layoutNeedUpdate || ![layout.attachmentContentsSet containsObject:view]) {
                    if (view.superview == self) {
                        [view removeFromSuperview];
                    }
                }
            }
            for (CALayer *layer in attachmentLayers) {
                if (layoutNeedUpdate || ![layout.attachmentContentsSet containsObject:layer]) {
                    if (layer.superlayer == self.layer) {
                        [layer removeFromSuperlayer];
                    }
                }
            }
            [attachmentViews removeAllObjects];
            [attachmentLayers removeAllObjects];
        };
    
        task.display = ^(CGContextRef context, CGSize size, BOOL (^isCancelled)(void)) {
            if (isCancelled()) return;
            if (text.length == 0) return;
            
            YYTextLayout *drawLayout = layout;
            if (layoutNeedUpdate) {
                layout = [YYTextLayout layoutWithContainer:container text:text];
                shrinkLayout = [YYLabel _shrinkLayoutWithLayout:layout];
                if (isCancelled()) return;
                layoutUpdated = YES;
                drawLayout = shrinkLayout ? shrinkLayout : layout;
            }
            
            CGSize boundingSize = drawLayout.textBoundingSize;
            CGPoint point = CGPointZero;
            if (verticalAlignment == YYTextVerticalAlignmentCenter) {
                if (drawLayout.container.isVerticalForm) {
                    point.x = -(size.width - boundingSize.width) * 0.5;
                } else {
                    point.y = (size.height - boundingSize.height) * 0.5;
                }
            } else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
                if (drawLayout.container.isVerticalForm) {
                    point.x = -(size.width - boundingSize.width);
                } else {
                    point.y = (size.height - boundingSize.height);
                }
            }
            point = CGPointPixelRound(point);
            [drawLayout drawInContext:context size:size point:point view:nil layer:nil debug:debug cancel:isCancelled];
        };
    
        task.didDisplay = ^(CALayer *layer, BOOL finished) {
            YYTextLayout *drawLayout = layout;
            if (layoutUpdated && shrinkLayout) {
                drawLayout = shrinkLayout;
            }
            if (!finished) {
                // If the display task is cancelled, we should clear the attachments.
                for (YYTextAttachment *a in drawLayout.attachments) {
                    if ([a.content isKindOfClass:[UIView class]]) {
                        if (((UIView *)a.content).superview == layer.delegate) {
                            [((UIView *)a.content) removeFromSuperview];
                        }
                    } else if ([a.content isKindOfClass:[CALayer class]]) {
                        if (((CALayer *)a.content).superlayer == layer) {
                            [((CALayer *)a.content) removeFromSuperlayer];
                        }
                    }
                }
                return;
            }
            [layer removeAnimationForKey:@"contents"];
            
            __strong YYLabel *view = (YYLabel *)layer.delegate;
            if (!view) return;
            if (view->_state.layoutNeedUpdate && layoutUpdated) {
                view->_innerLayout = layout;
                view->_shrinkInnerLayout = shrinkLayout;
                view->_state.layoutNeedUpdate = NO;
            }
            
            CGSize size = layer.bounds.size;
            CGSize boundingSize = drawLayout.textBoundingSize;
            CGPoint point = CGPointZero;
            if (verticalAlignment == YYTextVerticalAlignmentCenter) {
                if (drawLayout.container.isVerticalForm) {
                    point.x = -(size.width - boundingSize.width) * 0.5;
                } else {
                    point.y = (size.height - boundingSize.height) * 0.5;
                }
            } else if (verticalAlignment == YYTextVerticalAlignmentBottom) {
                if (drawLayout.container.isVerticalForm) {
                    point.x = -(size.width - boundingSize.width);
                } else {
                    point.y = (size.height - boundingSize.height);
                }
            }
            point = CGPointPixelRound(point);
            [drawLayout drawInContext:nil size:size point:point view:view layer:layer debug:nil cancel:NULL];
            for (YYTextAttachment *a in drawLayout.attachments) {
                if ([a.content isKindOfClass:[UIView class]]) [attachmentViews addObject:a.content];
                else if ([a.content isKindOfClass:[CALayer class]]) [attachmentLayers addObject:a.content];
            }
            
            if (contentsNeedFade) {
                CATransition *transition = [CATransition animation];
                transition.duration = kHighlightFadeDuration;
                transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
                transition.type = kCATransitionFade;
                [layer addAnimation:transition forKey:@"contents"];
            } else if (fadeForAsync) {
                CATransition *transition = [CATransition animation];
                transition.duration = kAsyncFadeDuration;
                transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
                transition.type = kCATransitionFade;
                [layer addAnimation:transition forKey:@"contents"];
            }
        };
        
        return task;
    }
    

    2.2、搜索newAsyncDisplayTask

    - (void)_displayAsync:(BOOL)async {
        __strong id<YYAsyncLayerDelegate> delegate = (id)self.delegate;
        //YYLabel
        YYAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask];
        //具体的绘制任务 task.display
        if (!task.display) {
            //即将显示
            if (task.willDisplay) task.willDisplay(self);
            self.contents = nil;
            //绘制结束!!
            if (task.didDisplay) task.didDisplay(self, YES);
            return;
        }
        
        if (async) {
            if (task.willDisplay) task.willDisplay(self);
            //计数器 : 标识当前的绘制任务!!!
            YYSentinel *sentinel = _sentinel;
            int32_t value = sentinel.value;
            //取消绘制的标识!
            BOOL (^isCancelled)(void) = ^BOOL() {
                return value != sentinel.value;
            };
            CGSize size = self.bounds.size;
            BOOL opaque = self.opaque;
            CGFloat scale = self.contentsScale;
            CGColorRef backgroundColor = (opaque && self.backgroundColor) ? CGColorRetain(self.backgroundColor) : NULL;
            if (size.width < 1 || size.height < 1) {
                CGImageRef image = (__bridge_retained CGImageRef)(self.contents);
                self.contents = nil;
                if (image) {
                    dispatch_async(YYAsyncLayerGetReleaseQueue(), ^{
                        CFRelease(image);
                    });
                }
                if (task.didDisplay) task.didDisplay(self, YES);
                CGColorRelease(backgroundColor);
                return;
            }
            
            //子线程
            dispatch_async(YYAsyncLayerGetDisplayQueue(), ^{
                if (isCancelled()) {
                    CGColorRelease(backgroundColor);
                    return;
                }
                //Core Graphics 合成一张图片
                UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
                CGContextRef context = UIGraphicsGetCurrentContext();
                if (opaque && context) {
                    CGContextSaveGState(context); {
                        if (!backgroundColor || CGColorGetAlpha(backgroundColor) < 1) {
                            CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
                            CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
                            CGContextFillPath(context);
                        }
                        if (backgroundColor) {
                            CGContextSetFillColorWithColor(context, backgroundColor);
                            CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
                            CGContextFillPath(context);
                        }
                    } CGContextRestoreGState(context);
                    CGColorRelease(backgroundColor);
                }
                task.display(context, size, isCancelled);
                if (isCancelled()) {
                    UIGraphicsEndImageContext();
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (task.didDisplay) task.didDisplay(self, NO);
                    });
                    return;
                }
                UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
                UIGraphicsEndImageContext();
                if (isCancelled()) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (task.didDisplay) task.didDisplay(self, NO);
                    });
                    return;
                }
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (isCancelled()) {
                        if (task.didDisplay) task.didDisplay(self, NO);
                    } else {
                        self.contents = (__bridge id)(image.CGImage);
                        if (task.didDisplay) task.didDisplay(self, YES);
                    }
                });
            });
        } else {
            [_sentinel increase];
            if (task.willDisplay) task.willDisplay(self);
            UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale);
            CGContextRef context = UIGraphicsGetCurrentContext();
            if (self.opaque && context) {
                CGSize size = self.bounds.size;
                size.width *= self.contentsScale;
                size.height *= self.contentsScale;
                CGContextSaveGState(context); {
                    if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) {
                        CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
                        CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
                        CGContextFillPath(context);
                    }
                    if (self.backgroundColor) {
                        CGContextSetFillColorWithColor(context, self.backgroundColor);
                        CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
                        CGContextFillPath(context);
                    }
                } CGContextRestoreGState(context);
            }
            task.display(context, self.bounds.size, ^{return NO;});
            UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            self.contents = (__bridge id)(image.CGImage);
            if (task.didDisplay) task.didDisplay(self, YES);
        }
    }
    
    • callers _displayAsync
    //子类重载,自定义绘制任务!!!CALayer 的方法!!!!
    - (void)display {
        super.contents = super.contents;
        //是否开启异步绘制!!!(线程!!)
        [self _displayAsync:_displaysAsynchronously];
    }
    

    相关文章

      网友评论

          本文标题:YYAsyncLayer

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