当前位置:网站首页>Why build a uilabel? (review liangya Technology)
Why build a uilabel? (review liangya Technology)
2022-07-23 16:18:00 【dengjiangszhan】
Mainly refer to YYKit
YYKit broad and profound , Like Shaolin Martial Arts
a, Async View
For asynchrony + runloop Draw when free ,
Because of Apple's UILabel Not enough performance 6
1, Async Layer
Ideas : UI operation , Must be placed on the main thread ,
However, graphics processing , It can be placed in the sub thread ,
( Open up graphic context , Drawing , Take out the picture )
The last step , Put it in the main thread , Just fine
layer.contents = image
Custom View in ,
- layer class , Reformulated as asynchronous layer
+ (Class)layerClass {
return YYAsyncLayer.class;
}
- Create a drawing task
Create a drawing task ,YYAsyncLayerDisplayTask
The key is the drawing method inside display
Get the asynchronous layer layer Created graphic context ,
Adjust the coordinate system ,( Core Text The origin of , Lower left )
Text map Is rich text ,
Rich text map For one frame ,
One frame is divided into many CTLine,
Line by line
- (YYAsyncLayerDisplayTask *)newAsyncDisplayTask {
// capture current state to display task
NSString *text = _text;
UIFont *fontX = _font;
YYAsyncLayerDisplayTask *task = [YYAsyncLayerDisplayTask new];
CGFloat h_h = self.bounds.size.height;
CGFloat w_w = self.bounds.size.width;
task.display = ^(CGContextRef context, CGSize size, BOOL(^isCancelled)(void)) {
if (isCancelled()) return;
// Here, because the drawing text will be reversed
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, h_h);
CGContextScaleCTM(context, 1.0, -1.0);
}];
NSAttributedString* str = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: fontX, NSForegroundColorAttributeName: UIColor.blueColor}];
CTFramesetterRef ref = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)str);
CGPathRef path = CGPathCreateWithRect(CGRectMake(0, 0, w_w, 3000), nil);
CTFrameRef pic = CTFramesetterCreateFrame(ref, CFRangeMake(0, 0), path, nil);
CFArrayRef arr = CTFrameGetLines(pic);
NSArray *array = (__bridge NSArray*)arr;
int i = 0;
int cnt = (int)array.count;
CGPoint originsArray[cnt];
CTFrameGetLineOrigins(pic, CFRangeMake(0, 0), originsArray);
CGFloat y_y = h_h - 60;
while (i < cnt) {
NSLog(@"%f", originsArray[i].y);
CTLineRef line = (__bridge CTLineRef)(array[i]);
CGContextSetTextPosition(context, 0, y_y - i * 30);
CTLineDraw(line, context);
i += 1;
}
};
return task;
}
Async Layer in ,
- Start the drawing task ,
First deal with the inheritance relationship ,
Then perform the drawing task mentioned above
- (void)display {
super.contents = super.contents;
[self _displayAsync];
}
- Perform the drawing task ,
Get the mission , No drawing content , Forget it
To determine , Own size ( size ), Compliance or non-compliance
size CGSize(1, 1), Just go ahead ,
- Sub thread , First open up the graphic context ,
Then deal with the background color ,
If all goes well , Perform the drawing steps above ,
From the graphic context , Take out image, hand layer.contents
- (void)_displayAsync{
__strong id<YYAsyncLayerDelegate> delegate = (id)self.delegate;
YYAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask];
if (!task.display) {
self.contents = nil;
return;
}
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);
});
}
CGColorRelease(backgroundColor);
return;
}
dispatch_async(YYAsyncLayerGetDisplayQueue(), ^{
if (isCancelled()) {
CGColorRelease(backgroundColor);
return;
}
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (opaque) {
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();
return;
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (isCancelled()) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if (isCancelled() == NO) {
self.contents = (__bridge id)(image.CGImage);
}
});
});
}
2, RunLoop
Trigger
After setting the style , Will not trigger immediately , Repaint
Save it first
- (void)setText:(NSString *)text {
_text = text.copy;
[[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit];
}
Call the drawing task of asynchronous layer
- (void)contentsNeedUpdated {
// do update
[self.layer setNeedsDisplay];
}
Save events
First call the method target and action, Save as object
YYTransactionSetup(); Single example method , initialization
The object that calls the method , Add to collection
@implementation YYTransaction
+ (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector{
if (!target || !selector) return nil;
YYTransaction *t = [YYTransaction new];
t.target = target;
t.selector = selector;
return t;
}
- (void)commit {
if (!_target || !_selector) return;
YYTransactionSetup();
[transactionSet addObject:self];
}
In my spare time , Get things done , Does not affect the frame rate
The following singleton method , Initialize the event task set ,
run loop Calling back , perform
Do not interfere , Lord runloop
static NSMutableSet *transactionSet = nil;
static void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
if (transactionSet.count == 0) return;
NSSet *currentSet = transactionSet;
transactionSet = [NSMutableSet new];
[currentSet enumerateObjectsUsingBlock:^(YYTransaction *transaction, BOOL *stop) {
[transaction.target performSelector:transaction.selector];
}];
}
static void YYTransactionSetup() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
transactionSet = [NSMutableSet new];
CFRunLoopRef runloop = CFRunLoopGetMain();
CFRunLoopObserverRef observer;
observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(),
kCFRunLoopBeforeWaiting | kCFRunLoopExit,
true,
0xFFFFFF,
YYRunLoopObserverCallBack, NULL);
CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes);
CFRelease(observer);
});
}
b, YYLabel
A very powerful rendering tool ,
On the basis of asynchronous rendering above
Support various styles
Add a layer of abstraction YYTextLayout
YYLabelDrawing tasks in ,
Update if necessary , Just create a new layout layout ,
If centered / Bottom to its , Deal with layout Layout
then layout draw
@implementation YYLabel
- (YYTextAsyncLayerDisplayTask *)newAsyncDisplayTask {
// create display task
YYTextAsyncLayerDisplayTask *task = [YYTextAsyncLayerDisplayTask new];
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 = YYTextCGPointPixelRound(point);
[drawLayout drawInContext:context size:size point:point view:nil layer:nil debug:debug cancel:isCancelled];
};
return task;
}
@end
- Draw various
First draw the background ,
Next, draw shadows ,
Underline ,
written words ,
picture
Frame
@implementation YYTextLayout
- (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);
}
}
}
- Enter draw text
And pictures
The rendering granularity here , Compare the above ,
The granularity is finer
Above is CTLine,
Here is CTRun
// Pay attention to conditional judgment ,
// And preservation / Restore graphic context
static void YYTextDrawAttachment(YYTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, UIView *targetView, CALayer *targetLayer, BOOL (^cancel)(void)) {
BOOL isVertical = layout.container.verticalForm;
CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0;
for (NSUInteger i = 0, max = layout.attachments.count; i < max; i++) {
YYTextAttachment *a = layout.attachments[i];
if (!a.content) continue;
UIImage *image = nil;
UIView *view = nil;
CALayer *layer = nil;
if ([a.content isKindOfClass:[UIImage class]]) {
image = a.content;
} else if ([a.content isKindOfClass:[UIView class]]) {
view = a.content;
} else if ([a.content isKindOfClass:[CALayer class]]) {
layer = a.content;
}
if (!image && !view && !layer) continue;
if (image && !context) continue;
if (view && !targetView) continue;
if (layer && !targetLayer) continue;
if (cancel && cancel()) break;
CGSize asize = image ? image.size : view ? view.frame.size : layer.frame.size;
CGRect rect = ((NSValue *)layout.attachmentRects[i]).CGRectValue;
if (isVertical) {
rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetRotateVertical(a.contentInsets));
} else {
rect = UIEdgeInsetsInsetRect(rect, a.contentInsets);
}
rect = YYTextCGRectFitWithContentMode(rect, asize, a.contentMode);
rect = YYTextCGRectPixelRound(rect);
rect = CGRectStandardize(rect);
rect.origin.x += point.x + verticalOffset;
rect.origin.y += point.y;
if (image) {
CGImageRef ref = image.CGImage;
if (ref) {
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, CGRectGetMaxY(rect) + CGRectGetMinY(rect));
CGContextScaleCTM(context, 1, -1);
CGContextDrawImage(context, rect, ref);
CGContextRestoreGState(context);
}
} else if (view) {
view.frame = rect;
[targetView addSubview:view];
} else if (layer) {
layer.frame = rect;
[targetLayer addSublayer:layer];
}
}
}
this paper , Last question :
above layout Drawing information for , How to get ?
Take the text first , establish CTFrame, CTFrame Get in the CTLine Array
Then go through each line , And calculation
@implementation YYTextLayout
+ (YYTextLayout *)layoutWithContainer:(YYTextContainer *)container text:(NSAttributedString *)text range:(NSRange)range {
// ...
ctSetter = CTFramesetterCreateWithAttributedString((CFTypeRef)text);
if (!ctSetter) goto fail;
ctFrame = CTFramesetterCreateFrame(ctSetter, YYTextCFRangeFromNSRange(range), cgPath, (CFTypeRef)frameAttrs);
if (!ctFrame) goto fail;
lines = [NSMutableArray new];
ctLines = CTFrameGetLines(ctFrame);
// ...
for (NSUInteger i = 0, max = lines.count; i < max; i++) {
YYTextLine *line = lines[i];
if (truncatedLine && line.index == truncatedLine.index) line = truncatedLine;
if (line.attachments.count > 0) {
[attachments addObjectsFromArray:line.attachments];
[attachmentRanges addObjectsFromArray:line.attachmentRanges];
[attachmentRects addObjectsFromArray:line.attachmentRects];
for (YYTextAttachment *attachment in line.attachments) {
if (attachment.content) {
[attachmentContentsSet addObject:attachment.content];
}
}
}
}
// ...
}
github repo
边栏推荐
- 死锁、饥饿、死循环之间的区别
- CS5363,CS5350,CS5328几款太阳能板电池充电管理IC的功能特性与参数对比
- ORA-01654错误:表空间满了,插入失败
- Of the 24 almost inevitable JVM interview questions, I only know 7. How many can you answer?
- 数字化转型时代的企业数据新基建 | 爱分析报告
- Redis master-slave replication
- ten thousand and one hundred
- LeetCode高频题:最少经过几次操作可以使数组变为非降序状态
- CA数字证书
- Fake XML cookbook of XML xxE vulnerability
猜你喜欢

table自定义表格的封装

ESP8266 NodeMCU 闪存文件系统(SPIFFS)

A quietly rising domestic software is too strong!

Idées de conception sur l'initialisation des paramètres d'entrée de page

CS5363,CS5350,CS5328几款太阳能板电池充电管理IC的功能特性与参数对比

VRRP+MSTP配置详解【华为eNSP实验】

SOC的第一个Hello_World实验
![[attack and defense world web] difficulty Samsung 9 points introductory question (Part 1): simple_ js、mfw](/img/72/d3e46a820796a48b458cd2d0a18f8f.png)
[attack and defense world web] difficulty Samsung 9 points introductory question (Part 1): simple_ js、mfw

Bean Validation起源篇----01

Bean validation core components - 04
随机推荐
[in simple terms] from self information to entropy, from relative entropy to cross entropy, nn Crossentropyloss, cross entropy loss function and softmax, multi label classification
Mathematical Modeling Typesetting
Kubernetes 基本概念和部署
The difference between deadlock, hunger and dead cycle
Umijs - data transmission between main and sub applications of Qiankun
1060 Are They Equal
死锁、饥饿、死循环之间的区别
黑马程序员-接口测试-四天学习接口测试-第三天-postman高级用法,newman例集导出导入,常用断言,断言json数据,工作原理,全局,环境变量,时间戳,请求前置脚本,关联,批量执行测试用例
Bean Validation规范篇----03
Learning summary of ugly code
From the big guy baptism! 2022 headline first hand play MySQL advanced notes, and it is expected to penetrate P7
Don't want dto set. Dto can be written like this
问题随记 —— 无法打开包括文件: “dirent.h”: No such file or directory
List merging (summer vacation daily question 3)
How to solve the problem of forgetting the Oracle password
在多个数字(有重复)中找到最小值以及所在位置
CONDA set up proxy
VRRP+MSTP配置详解【华为eNSP实验】
[attack and defense world web] difficulty Samsung 9-point introductory question (middle): ics-05, easytornado
如何成为一个优雅的硬件工程师?