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];
}
网友评论