当前位置: 首页 > 科技观察

MKMapView性能优化

时间:2023-03-12 03:34:32 科技观察

最近的项目是LBS的主要会员定位功能。我们的UI设计乍一看是这样的。不同的人会显示不同的头像,但是当人们聚在一起时,问题就来了。大多数时候(比如上图),在滑动地图的时候,可以感觉到明显的卡顿。不流畅的感觉会把人折磨死,自然要解决这个问题(等等,为什么不用地图聚合,因为这已经是地图了)聚合不适合讨论这个问题。)先分析,看看我是怎么实现这个annotationView的,因为这个annotationsView是有形状的(就是不能通过设置圆角直接得到),里面的图片和用户还是不一样的。解决方法是使用layer.mask屏蔽代码如下@implementationMMAnnotationView-(instancetype)initWithAnnotation:(id)annotationreuseIdentifier:(NSString*)reuseIdentifier{self=[superinitWithAnnotation:annotationreuseIdentifier:reuseIdentifier];if(self){self.frame=CGRectMake(0,0,TRACK_ANNOTATION_SIZE.width,TRACK_ANNOTATION_SIZE.height);self.centerOffset=CGPointMake(0,-(TRACK_ANNOTATION_SIZE.height-3)/2);self.canShowCallout=NO;self.avatarView=[[UIImageViewalloc]initWithFrame:self.bounds];[selfaddSubview:self.avatarView];self.avatarView.contentMode=UIViewContentModeScaleAspectFill;CAShapeLayer*shapelayer=[CAShapeLayerlayer];shapelayer.frame=self.bounds;shapelayer.path=self.framePath。CGPath;self.avatarView.layer.mask=shapelayer;self.layer.shadowPath=self.framePath.CGPath;self.layer.shadowRadius=1.0f;self.layer.shadowColor=[UIColorcolorWithHex:0x666666FF].CGColor;self.layer.shadowOpacity=1.0f;self.layer.shadowOffset=CGSizeMake(0,0);self.layer.masksToBounds=NO;}returnsself;}//掩码路径-(UIBezierPath*)framePath{if(!_framePath){CGFloatarrowWidth=14;CGMutablePathRefpath=CGPathCreateMutable();CGRectrectangle=CGRectInset(CGRectMake(0,0,CGRectGetWidth(self.bounds),CGRectGetWidth(self.bounds)),3,3);CGPointp[3]={{CGRectGetMidX(self.bounds)-arrowWidth/2,CGRectGetWidth(self.bounds)-6},{CGRectGetMidX(self.bounds)+arrowWidth/2,CGRectGetWidth(self.bounds)-6},{CGRectGetMidX(self.bounds),CGRectGetHeight(self.bounds)-4}};CGPathAddRoundedRect(path,NULL,rectangle,5,5);CGPathAddLines(path,NULL,p,3);CGPathCloseSubpath(path);_framePath=[UIBezierPathbezierPathWithCGPath:path];CGPathRelease(path);}return_framePath;}我用代码生成形状路径,并用它来生成图层的遮罩和阴影路径。使用时直接使用SDWebImage设置头像即可。1个[annotationView.avatarViewsd_setImageWithURL:[NSURURLWithString:avatarURL]placeholderImage:placeHolderImage];接下来,使用工具来分析问题。分析性能当然是选择Instruments(用法这里就不介绍了)打开CoreAnimation运行程序滑动地图看性能分析如下。原来的平均帧数不到30帧,离我们60帧的目标还差得很远。然后使用DebugOption深入分析,因为MKMapView。这里我们主要关心这几个选项:ColorBlendedLayersColorMisalignedImagesColorOffscreen-RenderedYellow开启这些选项,效果如下。可以看到ColorBlendedLayers没有问题,但这是正常的,因为使用了遮罩,没有透明度。ColorMisaligned除默认头像外,所有图像均已选中。这是因为服务器上的图像大小。与显示尺寸不一致导致缩放,而默认头像是一致的,所以没有问题。ColorOffscreen-RenderedYellow全区使用,导致大量离屏渲染。这也是性能下降的主要原因。找到解决问题的原因。那么接下来怎么解决呢?首先,口罩一定不能用。其次,我们需要把下载的图片预处理成实际大小,那么直接把下载的图片合成成我们要显示的最终结果就可以了吗?试一下-(void)loadAnnotationImageWithURL:(NSString*)urlimageView:(UIImageView*)imageView{//缓存合成图片NSString*annoImageURL=url;NSString*annoImageCacheURL=[annoImageURLstringByAppendingString:@"cache"];UIImage*cacheImage=[[SDImageCachesharedImageCache]imageFromDiskCacheForKey:annoImageCacheURL];if(cacheImage){//LLLog(@"hitcache");imageView.image=cacheImage;}else{//LLLog(@"nocache");[imageViewsd_setImageWithURL:[NSURLURLWithString:annoImageURL]placeholderImage:placeHolderImagecompleted:^(UIImage*image,NSError*error,SDImageCacheTypecacheType,NSURL*imageURL){if(!error){UIImage*annoImage=[imageannotationImage];imageView.image=annoImage;[[SDImageCachesharedImageCache]storeImage:annoImageforKey:annoImageCacheURL];}}];}}@implementationUIImage(LJC)-(UIImage*)annotationImage{staticUIView*snapshotView=nil;staticUIImageView*imageView=nil;if(!snapshotView){snapshotView=[UIViewnew];snapshotView.frame=CGRectMake(0,0,TRACK_ANNOTATION_SIZE.width,TRACK_ANNOTATION_SIZE.高度);imageView=[UIImageViewnew];[snapshotViewaddSubview:imageView];imageView.clipsToBounds=YES;imageView.frame=snapshotView.bounds;imageView.contentMode=UIViewContentModeScaleAspectFill;CGFloatarrowWidth=14;CGMutablePathRefpath=CGPathCreateMutable();CGRectrectangle=CGRectInset(CGRectMake(0,0,CGRectGetWidth(imageView.bounds),CGRectGetWidth(imageView.bounds)),3,3);CGPointp[3]={{CGRectGetMidX(imageView.bounds)-arrowWidth/2,CGRectGetWidth(imageView.边界)-6},{CGRectGetMidX(imageView.bounds)+arrowWidth/2,CGRectGetWidth(imageView.bounds)-6},{CGRectGetMidX(imageView.bounds),CGRectGetHeight(imageView.bounds)-4}};CGPathAddRoundedRect(路径,NULL,rectangle,5,5);CGPathAddLines(path,NULL,p,3);CGPathCloseSubpath(path);CAShapeLayer*shapelayer=[CAShapeLayerlayer];shapelayer.frame=imageView.bounds;shapelayer.path=path;imageView。layer.mask=shapelayer;snapshotView.layer.shadowPath=path;snapshotView.layer.shadowRadius=1.0f;snapshotView.layer.shadowColor=[UIColorcolorWithHex:0x666666FF].CGColor;snapshotView.layer.shadowOpacity=1.0f;snapshotView.layer。shadowOffset=CGSizeMake(0,0);CGPathRelease(path);}imageView.image=self;UIGraphicsBeginImageContextWithOptions(TRACK_ANNOTATION_SIZE,NO,0);[snapshotView.layerrenderInContext:UIGraphicsGetCurrentContext()];UIImage*copied=UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();returncopied;}@end然后在使用的时候只需要简单调用如下[selfloadAnnotationImageWithURL:avatarURLimageView:annotationView.avatarView];看看修改后的Instruments如何进行ColorBlendedLayers是不可避免的,因为显示的是一张透明的地图,但是由于地图的特殊性(头像的位置变化间隔很长,所以不会经常造成synthesisandnoanimation)所以这里也没有问题。ColorMisalignedImages没有问题,因为头像已缩放到相同大小。ColorOffscreen-RenderedYellow没有问题,因为它只是显示一张图片,不需要离屏渲染。再来看看帧率Oh-Yeah~不仅帧数达到了我们的目标60帧(因为后台还有业务逻辑线程在运行,所以不太稳定),平均运行时间也有即使在地图上显示几十个人也掉了很多这不是问题。总结不仅仅是MKMapView。其实很多地方,包括UITableView,都可以用文中提到的方法进行优化。核心点是合成+缓存。当然,合成还是要消耗一些资源的,所以比较适合这种小分身。Resources图形性能优化,可以看这篇好文(文中提到的DebugOption不理解的有详细解释)