做设计赚钱的网站,全国十大摄影培训机构,北京工程信息交易网,网站实施方案引起UITableView卡顿比较常见的原因有cell的层级过多、cell中有触发离屏渲染的代码#xff08;譬如#xff1a;cornerRadius、maskToBounds 同时使用#xff09;、像素是否对齐、是否使用UITableView自动计算cell高度的方法等。本文将从cell层级出发#xff0c;以一个仿朋友… 引起UITableView卡顿比较常见的原因有cell的层级过多、cell中有触发离屏渲染的代码譬如cornerRadius、maskToBounds 同时使用、像素是否对齐、是否使用UITableView自动计算cell高度的方法等。本文将从cell层级出发以一个仿朋友圈的demo来讲述如何让列表保持顺滑项目的源码可在文末获得。不可否认的是过早的优化是魔鬼请在项目出现性能瓶颈再考虑优化。 首先看看reveal上页面层级的效果图 1、绘制文本 使用core text可以将文本绘制在一个CGContextRef上最后再通过UIGraphicsGetImageFromCurrentImageContext()生成图片再将图片赋值给cell.contentView.layer从而达到减少cell层级的目的。 绘制普通文本譬如用户昵称在context上相关注释在代码里 - (void)drawInContext:(CGContextRef)context withPosition:(CGPoint)p andFont:(UIFont *)font andTextColor:(UIColor *)color andHeight:(float)height andWidth:(float)width lineBreakMode:(CTLineBreakMode)lineBreakMode { CGSize size CGSizeMake(width, height); // 翻转坐标系 CGContextSetTextMatrix(context,CGAffineTransformIdentity); CGContextTranslateCTM(context,0,height); CGContextScaleCTM(context,1.0,-1.0); NSMutableDictionary * attributes [StringAttributes attributeFont:font andTextColor:color lineBreakMode:lineBreakMode]; // 创建绘制区域路径 CGMutablePathRef path CGPathCreateMutable(); CGPathAddRect(path,NULL,CGRectMake(p.x, height-p.y-size.height,(size.width),(size.height))); // 创建AttributedString NSMutableAttributedString *attributedStr [[NSMutableAttributedString alloc] initWithString:self attributes:attributes]; CFAttributedStringRef attributedString (__bridge CFAttributedStringRef)attributedStr; // 绘制frame CTFramesetterRef framesetter CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedString); CTFrameRef ctframe CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0),path,NULL); CTFrameDraw(ctframe,context); CGPathRelease(path); CFRelease(framesetter); CFRelease(ctframe); [[attributedStr mutableString] setString:]; CGContextSetTextMatrix(context,CGAffineTransformIdentity); CGContextTranslateCTM(context,0, height); CGContextScaleCTM(context,1.0,-1.0); } 绘制朋友圈内容文本带链接在context上这里我还没有去实现文本多了会折叠的效果与上面普通文本不同的是这里需要创建带链接的AttributeString和CTLineRef的逐行绘制 - (NSMutableAttributedString *)highlightText:(NSMutableAttributedString *)coloredString{ // 创建带高亮的AttributedString NSString* string coloredString.string; NSRange range NSMakeRange(0,[string length]); NSDataDetector *linkDetector [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil]; NSArray *matches [linkDetector matchesInString:string options:0 range:range]; for(NSTextCheckingResult* match in matches) { [self.ranges addObject:NSStringFromRange(match.range)]; UIColor *highlightColor UIColorFromRGB(0x297bc1); [coloredString addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)highlightColor.CGColor range:match.range]; } return coloredString; } - (void)drawFramesetter:(CTFramesetterRef)framesetter attributedString:(NSAttributedString *)attributedString textRange:(CFRange)textRange inRect:(CGRect)rect context:(CGContextRef)c { CGMutablePathRef path CGPathCreateMutable(); CGPathAddRect(path, NULL, rect); CTFrameRef frame CTFramesetterCreateFrame(framesetter, textRange, path, NULL); CGFloat ContentHeight CGRectGetHeight(rect); CFArrayRef lines CTFrameGetLines(frame); NSInteger numberOfLines CFArrayGetCount(lines); CGPoint lineOrigins[numberOfLines]; CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); // 遍历每一行 for (CFIndex lineIndex 0; lineIndex numberOfLines; lineIndex) { CGPoint lineOrigin lineOrigins[lineIndex]; CTLineRef line CFArrayGetValueAtIndex(lines, lineIndex); CGFloat descent 0.0f, ascent 0.0f, lineLeading 0.0f; CTLineGetTypographicBounds((CTLineRef)line, ascent, descent, lineLeading); CGFloat penOffset (CGFloat)CTLineGetPenOffsetForFlush(line, NSTextAlignmentLeft, rect.size.width); CGFloat y lineOrigin.y - descent - self.font.descender; // 设置每一行位置 CGContextSetTextPosition(c, penOffset self.xOffset, y - self.yOffset); CTLineDraw(line, c); // CTRunRef同一行中文本的不同样式包括颜色、字体等此处用途为处理链接高亮 CFArrayRef runs CTLineGetGlyphRuns(line); for (int j 0; j CFArrayGetCount(runs); j) { CGFloat runAscent, runDescent, lineLeading1; CTRunRef run CFArrayGetValueAtIndex(runs, j); NSDictionary *attributes (__bridge NSDictionary*)CTRunGetAttributes(run); // 判断是不是链接 if (!CGColorEqualToColor((__bridge CGColorRef)([attributes valueForKey:CTForegroundColor]), self.textColor.CGColor)) { CFRange range CTRunGetStringRange(run); float offset CTLineGetOffsetForStringIndex(line, range.location, NULL); // 得到链接的CGRect CGRect runRect; runRect.size.width CTRunGetTypographicBounds(run, CFRangeMake(0,0), runAscent, runDescent, lineLeading1); runRect.size.height self.font.lineHeight; runRect.origin.x lineOrigin.x offset self.xOffset; runRect.origin.y lineOrigin.y; runRect.origin.y - descent self.yOffset; // 因为坐标系被翻转链接正常的坐标需要通过CGAffineTransform计算得到 CGAffineTransform transform CGAffineTransformMakeTranslation(0, ContentHeight); transform CGAffineTransformScale(transform, 1.f, -1.f); CGRect flipRect CGRectApplyAffineTransform(runRect, transform); // 保存是链接的CGRect NSRange nRange NSMakeRange(range.location, range.length); self.framesDict[NSStringFromRange(nRange)] [NSValue valueWithCGRect:flipRect]; // 保存同一条链接的不同CGRect用于点击时背景色处理 for (NSString *rangeString in self.ranges) { NSRange range NSRangeFromString(rangeString); if (NSLocationInRange(nRange.location, range)) { NSMutableArray *array self.relationDict[rangeString]; if (array) { [array addObject:NSStringFromCGRect(flipRect)]; self.relationDict[rangeString] array; } else { self.relationDict[rangeString] [NSMutableArray arrayWithObject:NSStringFromCGRect(flipRect)]; } } } } } } CFRelease(frame); CFRelease(path); } 上述方法运用起来就是 这样就完成了文本的显示。 2、显示图片 图片包括用户头像和朋友圈的内容这里只是将CALayer添加到contentView.layer上具体做法是继承了CALayer实现部分功能。 通过链接显示图片 - (void)setContentsWithURLString:(NSString *)urlString { self.contents (__bridge id _Nullable)([UIImage imageNamed:placeholder].CGImage); weakify(self) SDWebImageManager *manager [SDWebImageManager sharedManager]; [manager downloadImageWithURL:[NSURL URLWithString:urlString] options:SDWebImageCacheMemoryOnly progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (image) { strongify(self) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if (!_observer) { _observer CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { self.contents (__bridge id _Nullable)(image.CGImage); }); if (_observer) { CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes); } } }); self.originImage image; } }]; } 其他比较简单就不展开。 3、显示小视频 之前的一篇文章简单讲了怎么自己做一个播放器这里就派上用场了。而显示小视频封面图片的CALayer同样在显示小视频的时候可以复用。 这里使用了NSOperationQueue来保障播放视频的流畅性具体继承NSOperation的VideoDecodeOperation相关代码如下 解码图片是因为UIImage在界面需要显示的时候才开始解码这样可能会造成主线程的卡顿所以在子线程对其进行解压缩处理。 具体的使用 4、其他 1、触摸交互是覆盖了以下方法实现 - (void)touchesCancelled:(NSSetUITouch * *)touches withEvent:(UIEvent *)event - (void)touchesCancelled:(NSSetUITouch * *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSetUITouch * *)touches withEvent:(UIEvent *)event 2、页面上FPS的测量是使用了YYKit项目中的YYFPSLabel。 3、测试数据是微博找的其中小视频是Gif快手。 本文的代码在https://github.com/hawk0620/PYQFeedDemo 转载于:https://www.cnblogs.com/fengmin/p/5669002.html