preface

  • UITableView is a control we often use, so it is necessary to optimize this section, there is a lot of information on this section optimization, in fact, the core essence is to reduce the CPU and GPU work to improve performance

CPU: object creation and destruction, object attribute adjustment, layout calculation, text calculation and typesetting, image format conversion and decoding, image rendering GPU: texture rendering

CPU layer optimization

1. Use lightweight objects

For example, where event handling is not needed, consider using a CALayer instead of a UIView

CALayer * imageLayer = [CALayer layer]; ,0,200,100 imageLayer. Bounds = CGRectMake (0); ImageLayer. Position = CGPointMake (200200); imageLayer.contents = (id)[UIImage imageNamed:@"xx.jpg"].CGImage; imageLayer.contentsGravity = kCAGravityResizeAspect; [tableCell.contentView.layer addSublayer:imageLayer];Copy the code

2. Do not frequently call UIView properties

Properties such as Frame, bounds, transform, etc., to minimize unnecessary changes do not dynamically add subviews to the UITableViewCell, you can initialize the UITableViewCell when all the need to show the added. Then set the hidden property to show and hide as needed

3. Calculate your layout in advance

UITableViewCell height calculation is mainly divided into two types, one is fixed height, the other is dynamic height.

Fixed height:

RowHeight height directly using the self. The default 44 for fixed tableView. RowHeight = 77 than tableView: heightForRowAtIndexPath: more efficient

Dynamic height:

The tableView: heightForRowAtIndexPath: this way of acting, set this agency after rowHeight is invalid, the following three conditions to be met

  • Use Autolayout for UI layout constraints (require that all four sides of the Cell. contentView have constraints on internal elements)
  • Specify the TableViewestimatedRowHeightProperty
  • Specify the TableViewrowHeightProperties forUITableViewAutomaticDimension
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44;
Copy the code

In addition to improving the computational efficiency of the cell height, we need to cache the calculated height

4. Set frame directly

Autolayout consumes more CPU than setting the frame directly

5. The picture is the right size

The image size should be exactly the same as the size of the UIImageView, and the image is processed through the contentMode, which also affects the scrolling speed of the TableView

  • After downloading the image from the network, cut/compress the image into a suitable size according to the size of the image to be displayed. Only the processed image is displayed each time. When viewing the larger image, the larger image is displayed.
  • The server directly returns preprocessed small and large images and the corresponding size is best
+ (UIImage*)kj_cutImageWithImage:(UIImage*)image Frame:(CGRect)cropRect{return ({CGImageRef TMP = CGImageCreateWithImageInRect([image CGImage], cropRect); UIImage *newImage = [UIImage imageWithCGImage:tmp scale:image.scale orientation:image.imageOrientation]; CGImageRelease(tmp); newImage; }); }Copy the code

6. Control the maximum number of concurrent requests

Control the maximum number of concurrent threads. When the number of download threads exceeds 2, it can significantly affect the performance of the main thread. When using ASIHTTPRequest accordingly, can use a NSOperationQueue download requests to maintain, and its maximum number of threads maxConcurrentOperationCount. NSURLRequest can be implemented in conjunction with GCD advanced technique sharing, or by using the setDelegateQueue: method of NSURLConnection. Of course, you can increase the number of download threads to speed up the download when you don’t need to respond to user requests:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (! decelerate) self.queue.maxConcurrentOperationCount = 5; } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ self.queue.maxConcurrentOperationCount = 5; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ self.queue.maxConcurrentOperationCount = 2; }Copy the code

7. Child thread processing

Try to place time-consuming operations on child threads

  • Text processing (size calculation, drawing)
  • Image processing (decoding, drawing)

8. Pre-render images

Extracting and resampling images takes a lot of CPU time. When you have an image, you draw it in the Bitmap context, export it as a UIImage object, and then draw it to the screen, which greatly speeds up rendering.

- (void)awakeFromNib { if (self.image == nil) { self.image = [UIImage imageNamed:@"xxx"]; UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); [image drawInRect:imageRect]; self.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); }}Copy the code

9. Draw asynchronously

Asynchronous drawing is an asynchronous drawing of the content on the canvas, and the complex drawing process is executed in a background thread and then displayed on the main thread

// Draw asynchronously, Switch to the child thread dispatch_async (dispatch_get_global_queue (0, 0), ^ {UIGraphicsBeginImageContextWithOptions (size, NO scale); CGContextRef context = UIGraphicsGetCurrentContext(); // TODO:draw in context... CGImageRef imgRef = CGBitmapContextCreateImage(context); UIGraphicsEndImageContext(); dispatch_async(dispatch_get_main_queue(), ^{ self.layer.contents = imgRef; }); });Copy the code

This article ios-UIView asynchronous drawing introduced full detail, of course, or little YY great god’s good work, iOS to keep the interface smooth skills (reprint)

10. Load as required

When you slide a UITableView, the corresponding content is loaded on demand

// Load on demand - if the target line differs from the current line by more than the specified number of lines, specify only 3 lines before and after the target scroll range to load. - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{ NSIndexPath *ip = [self indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)]; NSIndexPath *cip = [[self indexPathsForVisibleRows] firstObject]; NSInteger skipCount = 10; if (labs(cip.row-ip.row) > skipCount) { NSArray *temp = [self indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, self.width, self.height)]; NSMutableArray *arr = [NSMutableArray arrayWithArray:temp]; if (velocity.y < 0) { NSIndexPath *indexPath = [temp lastObject]; if (indexPath.row > 3) { [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]]; [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]]; [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]]; } } [self.needLoadDatas addObjectsFromArray:arr]; }}Copy the code

Also need the tableView: cellForRowAtIndexPath: adding judgment method

If (self.needloadDatas. count > 0 && [self.needloadDatas indexOfObject:indexPath] == NSNotFound) {//TODO: clean up work return; }Copy the code

GPU optimization

1. Avoid displaying too many pictures in a short period of time

Combine as many images as you can into one display

  • RunLoop small operation

When the current thread is the main thread, some UI events, such as the ScrollView is dragging, will RunLoop switch into NSEventTrackingRunLoopMode mode, in this mode the default NSDefaultRunLoopMode mode is not registered in the execution

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; if (! cell) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; } cell.selectionStyle = UITableViewCellSelectionStyleNone; KJTestModel *model = self.datas[indexPath.row]; if (model.iconImage) { cell.imageView.image = model.iconImage; }else{ NSDictionary *dict = @{@"imageView":cell.imageView,@"model":model}; [self performSelector:@selector(kj_loadImageView: withObject:dict afterDelay:0.0 inModes:@[NSDefaultRunLoopMode]]; } cell.nameLabel.text = model.name; cell.IDLabel.hidden = model.remarkName == nil ? YES : NO; cell.label.text = model.remarkName; Dict (NSDictionary*)dict{UIImageView *imageView = dict[@"imageView"]; [imageView sd_setImageWithURL:model.avatar completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { KJTestModel *model = dict[@"model"]; model.iconImage = image; }]; }Copy the code

2. Control size

The maximum texture size that GPU can process is 4096×4096. If it exceeds this size, CPU resources will be occupied for processing, so try not to exceed this size for textures

3. Reduce layer blending

When multiple views are superimposed, the view placed on top is translucent, so the GPU must mix the transparent color with the color placed on the bottom to produce a color and then display it on the screen. This step consumes GPU resources

  • Do not set the backgroundColor of UIView to clearColor. It is better to set it to the same backgroundColor as the superView.
  • Images Avoid images with alpha channels

4. Be transparent

To reduce transparent views, set Opaque = YES for opaque views

5. Avoid off-screen rendering

Off-screen rendering is the whole process of creating a new buffer outside the current screen buffer to operate off-screen rendering. It needs to switch the context several times, first from the current screen to off-screen; After the off-screen rendering is finished, the render results of the off-screen buffer are displayed on the screen, and the context needs to be switched from off-screen to the current screen

1 – The following situation or action will cause off-screen rendering

  • Rasterize, layer.shouldRasterize = YES
  • Mask the layer mask
  • Round the corners and set layer.masksToBounds = YES and layer.cornerRadius > 0
  • Shadow, layer shadow
  • Layer.allowsgroupopacity = YES and layer.opacity! = 1
  • Override the drawRect method

2 – Fillet optimization

With layer.maskstobounds = YES and layer.cornerRadius > 0, this is a cornerRadius off screen render. You can just use view.layer.cornerRadius = 3.0, and it will not render off screen, but UIImageView is a little bit special, you have to set both of these to make a corner, so it will render off screen, So we consider using CoreGraphics to crop the rounded corners, or using artists to provide rounded corners

- (UIImage *) kj_ellipseImage {UIGraphicsBeginImageContextWithOptions (self. The size, NO, 0.0); CGContextRef ctx = UIGraphicsGetCurrentContext(); CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); CGContextAddEllipseInRect(ctx, rect); CGContextClip(ctx); [self drawInRect:rect]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }Copy the code

Hollow out the circular image covering, this method can achieve the circular head effect, this is also a very efficient method. The disadvantage is that the background of the view has requirements, monochrome background effect is the most ideal

3 – Shadow optimization

For shadow, if the layer is a simple geometric shape or rounded shape, we can optimize the performance by setting shadowPath, which can greatly improve the performance

imageView.layer.shadowColor = [UIColor grayColor].CGColor; ImageView. Layer. ShadowOpacity = 1.0; ImageView. Layer. ShadowRadius = 2.0; UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame]; imageView.layer.shadowPath = path.CGPath;Copy the code

4 – Force rasterization on

ShouldRasterize = YES When rasterize is enabled, a bitmap cache will be generated the first time, and the cache will be reused when the image is used again. However, if the layer changes, the bitmap cache will be created again. Therefore, this feature cannot be used in UITableViewCell in general, and reuse reduces performance. Best used for graphics with more layers of static content

5 – Optimization suggestions

  • Use the transparent image in the middle to mask the corners
  • Use ShadowPath to specify the path of the layer shadow effects
  • Use asynchrony for layer rendering
  • Set the Opaque property of UITableViewCell and its subviews to YES to reduce complex layer composition
  • Try to use image resources that do not contain transparent alpha channels
  • Try to set the size of the layer to an integer
  • The background color should have an alpha value of 1; for example, do not use clearColor
  • It’s the most efficient way to just let the artist cut the image into rounded corners for display
  • In many cases, users upload images for display and let the server handle the rounded corners

Finally, some commonly used properties of TableViewCell are briefly introduced

function API & Property
Set the divider color [tableView setSeparatorColor:UIColor.orangeColor]
Set the divider style tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine
Whether multiple selections are allowed tableView.allowsMultipleSelection
Whether to respond to click action tableView.allowsSelection = YES
Returns the selected rows tableView.indexPathsForSelectedRows
Visible lines tableView.indexPathsForVisibleRows

This is the end of the introduction of UITableView performance optimization, there are related to add, write article is not easy, also please click **Little stars* * portal

Note: Some of the functions and Demo used in this article are from the tripartite library **KJEmitterView**, if there is a need for friends can bepod 'KJEmitterView'Introduction to