The most recently done project is lbs, the main member positioning function, which is our UI design.
At first glance, it looks good. Different people will show different avatars but when people get together, the problem comes.
When there are many people (for example, the picture above), the map slides up and it feels obvious that the discomfort of the card can torture the dead, so naturally we to solve this problem (wait, don't spit it out. Why not use map aggregation because this is already the map to the maximum aggregation is not suitable for this issue discussion)
Analysis
First look at how I realized this annotationview because this annotationsview is alien (that is, can not be set by the rounded corners directly to get) and the picture inside also because of users and different so the solution is to use Layer.mask to mask the code as follows
@implementation Mmannotationview
-(Instancetype) Initwithannotation: (id) annotation Reuseidentifier: (NSString *) reuseidentifier
{
self = [super initwithannotation:annotation reuseidentifier: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 = [[Uiimageview alloc] initWithFrame:self.bounds];
[Self addSubview:self.avatarView];
Self.avatarView.contentMode = Uiviewcontentmodescaleaspectfill;
Cashapelayer *shapelayer = [Cashapelayer layer];
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 = [Uicolor colorwithhex:0x666666ff]. Cgcolor;
self.layer.shadowOpacity = 1.0f;
Self.layer.shadowOffset = cgsizemake (0, 0);
Self.layer.masksToBounds = NO;
}
return self;
}
Mask path
-(Uibezierpath *) Framepath
{
if (!_framepath)
{
CGFloat arrowwidth = 14;
Cgmutablepathref path = cgpathcreatemutable ();
CGRect rectangle = cgrectinset (cgrectmake (0, 0, cgrectgetwidth (self.bounds), Cgrectgetwidth (Self.bounds)), 3,3);
Cgpoint P[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 = [Uibezierpath Bezierpathwithcgpath:path];
Cgpathrelease (path);
}
return _framepath;
}
I generated the shape path using code and generated the layer mask and Shadowpath
When you use it, just use Sdwebimage to set the avatar.
1
[Annotationview.avatarview Sd_setimagewithurl:[nsurl Urlwithstring:avatarurl] placeholderimage:placeholderimage];
Then use the tools to analyze the problem. The analysis performance of course is to choose instrments (usage is not introduced here) open the core Animation and then run the program slide map can see the performance analysis as follows
The original average frame number is less than 30 frames, which is far from our target of 60 frames.
And then use debug option to drill down on
Because of the mkmapview reason here we are mainly concerned with these several options
Color Blended Layers
Color misaligned Images
Color offscreen-rendered Yellow
Opening these options separately results in the following
Can see
Color blended layers no problem, but it's normal. Because of the use of mask no transparent place
Color misaligned images except the default avatar this is because the size of the picture on the server is inconsistent with the size of the display, and the default avatar is the same, so no problem.
Color offscreen-rendered Yellow Because of the use of mask led to a large number of off-screen rendering this is the main reason for performance degradation
Solve
The cause of the problem has been found then how to solve it then?
First of all, mask is definitely not going to work.
Next download down the picture we want to preprocess into the actual size
So just put the downloaded pictures into the final results we want to display is not OK? Try
-(void) Loadannotationimagewithurl: (nsstring*) URL ImageView: (uiimageview*) ImageView
{
To cache the synthesized picture
NSString *annoimageurl = URL;
NSString *annoimagecacheurl = [Annoimageurl stringbyappendingstring:@ "Cache"];
UIImage *cacheimage = [[Sdimagecache Sharedimagecache] imagefromdiskcacheforkey:annoimagecacheurl];
if (cacheimage)
{
Lllog (@ "hit cache");
Imageview.image = Cacheimage;
}
Else
{
Lllog (@ "no cache");
[ImageView Sd_setimagewithurl:[nsurl Urlwithstring:annoimageurl]
Placeholderimage:placeholderimage
completed:^ (UIImage *image, Nserror *error, Sdimagecachetype cachetype, Nsurl *imageurl) {
if (!error)
{
UIImage *annoimage = [Image annotationimage];
Imageview.image = Annoimage;
[[Sdimagecache Sharedimagecache] Storeimage:annoimage Forkey:annoimagecacheurl];
}
}];
}
}
@implementation UIImage (LJC)
-(uiimage*) annotationimage
{
static UIView *snapshotview = nil;
static Uiimageview *imageview = nil;
if (!snapshotview)
{
Snapshotview = [UIView new];
Snapshotview.frame = CGRectMake (0, 0, track_annotation_size.width, track_annotation_size.height);
ImageView = [Uiimageview new];
[Snapshotview Addsubview:imageview];
Imageview.clipstobounds = YES;
Imageview.frame = Snapshotview.bounds;
Imageview.contentmode = Uiviewcontentmodescaleaspectfill;
CGFloat arrowwidth = 14;
Cgmutablepathref path = cgpathcreatemutable ();
CGRect rectangle = cgrectinset (cgrectmake (0, 0, cgrectgetwidth (imageview.bounds), Cgrectgetwidth (Imageview.bounds)), 3,3);
Cgpoint P[3] = {
{Cgrectgetmidx (imageview.bounds)-ARROWWIDTH/2, Cgrectgetwidth (imageview.bounds)-6},
{Cgrectgetmidx (imageview.bounds) +ARROWWIDTH/2, Cgrectgetwidth (imageview.bounds)-6},
{Cgrectgetmidx (imageview.bounds), Cgrectgetheight (Imageview.bounds)-4}
};
Cgpathaddroundedrect (path, NULL, Rectangle, 5, 5);
Cgpathaddlines (Path, NULL, p, 3);
Cgpathclosesubpath (path);
Cashapelayer *shapelayer = [Cashapelayer layer];
Shapelayer.frame = Imageview.bounds;
Shapelayer.path = path;
ImageView.layer.mask = Shapelayer;
SnapshotView.layer.shadowPath = path;
SnapshotView.layer.shadowRadius = 1.0f;
SnapshotView.layer.shadowColor = [Uicolor colorwithhex: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.layer Renderincontext:uigraphicsgetcurrentcontext ()];
UIImage *copied = Uigraphicsgetimagefromcurrentimagecontext ();
Uigraphicsendimagecontext ();
return copied;
}
@end
And then use it as long as the simple following call is OK
[Self loadannotationimagewithurl:avatarurl imageView:annotationView.avatarView];
See how the instruments behaved after the change.
Color blended layers All of this is unavoidable because it shows a picture with transparency. But because of the specificity of the map (the avatar position varies a long interval so it doesn't often cause compositing or animation) so it's not a problem here.
Color misaligned images No problem, because the avatar has been scaled to the same size
Color offscreen-rendered Yellow No problem, because it simply shows a picture and there's no need to leave the screen to render the thing.
Let's look at the number of frames.
Oh-yeah~ not only the number of frames to reach our target 60 frames (because there are also business logic threads running in the background so not so stable) even the average running time has dropped a lot even if the map to show more than dozens of people is not a problem
Summary
Not only mkmapview in fact, including UITableView in many places can be described in the method to optimize its core point is the synthesis + cache of course, because of the synthesis or will consume a portion of resources, so it is more suitable for the head of this small resource
About graphic performance optimization You can look at this good article (there is a detailed explanation for the debug option that is mentioned in the article)