Say goodbye to iOS and common off-screen rendering!

Say goodbye to iOS and common off-screen rendering!

The optimization of mobile applications mainly depends on FPS (page smoothness) performance, memory usage, etc. Off-screen rendering is also a common problem. This article focuses on the common factors that lead to off-screen rendering and their solutions.

So why does off-screen rendering cause performance issues?

In OpenGL, there are two ways of GPU screen rendering: On-Screen Rendering and Off-Screen Rendering. On-Screen rendering does not require the creation of a new cache or the opening of a new context, and has better performance than off-screen rendering. However, due to the limitations of current screen rendering (only its own context, limited screen cache, etc.), off-screen rendering is used when current screen rendering cannot solve some problems. The entire off-screen rendering process requires switching the context environment, first switching from the current screen to the off-screen, and then switching the context environment back after the end. This is why performance is consumed.

[[214033]]

The factors that trigger off-screen rendering include cornerRadius (setting rounded corners), shadows, masks, edge antialiasing, group opacity, shouldRasterize, etc. As for the tool Instruments' Core Animation for detecting off-screen rendering, I won't go into details. This article mainly introduces the solution for setting rounded corners and shadows.

Set rounded corners

Conventional practice:

  1. //You only need to set two properties of the layer
  2. //Set the rounded corners
  3. imageView.layer.cornerRadius = imageView.frame. size .width / 2;
  4. //Cut off the excess
  5. imageView.layer.masksToBounds = YES;

Here are two solutions to avoid off-screen rendering

1. Add a sublayer to the top layer of the view to cover the view and its subviews. Set the layer's image to just cover the required rounded corners, and the image color is exactly the background color of the parent view of the view to achieve the desired effect.

github address

  1. /**
  2. Set a four-corner rounded corner
  3.  
  4. @param radius fillet radius
  5. @param color rounded background color
  6. */
  7. - (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color;
  8.  
  9. /**
  10. Set a normal rounded corner
  11.  
  12. @param radius fillet radius
  13. @param color rounded background color
  14. @param corners rounded corner position
  15. */
  16. - (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color corners:(UIRectCorner)corners;
  17.  
  18. /**
  19. Set a rounded corner with a border
  20.  
  21. @param cornerRadii corner radius cornerRadii
  22. @param color rounded background color
  23. @param corners rounded corner position
  24. @param borderColor border color
  25. @param borderWidth border line width
  26. */
  27. - (void)xw_roundedCornerWithCornerRadii:(CGSize)cornerRadii cornerColor:(UIColor *)color corners:(UIRectCorner)corners borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;  

After downloading this category, you can directly drag it into the project to use it. It is very convenient to call. However, when using it, you will find that these three APIs all need to pass a parameter cornerColor (the background color of the parent view), which also causes the limitation of this function. That is, if the color of the parent view is not a solid color, this method is not applicable at this time. Similarly, if the color of the parent view changes, the implementation code is not so elegant, as shown in the figure below, which is a bit embarrassing. Here comes the second solution.


Corner color does not match background color

2. By modifying layer.mask, first create a vector-based path through Bezier curves and pass it to CAShapeLayer for rendering. The path is closed, and then the drawn Shape is assigned to layer.mask. The layer outside the Mask range will not be displayed to achieve a rounded corner effect. The code implementation is very simple, as follows:

  1. UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(130, 330, 100, 100)];
  2. [btn setBackgroundColor:[UIColor colorWithRed:(226.0 / 255.0) green:(113.0 / 255.0) blue:(19.0 / 255.0) alpha:1]];
  3. [backgroundImageView addSubview:btn];
  4. //Draw the curve path
  5. UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:btn.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii: btn.bounds.size ];
  6. CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
  7. //Set the size
  8. maskLayer.frame = btn.bounds;
  9. //Set the graphics appearance
  10. maskLayer.path = maskPath.CGPath;
  11. btn.layer.mask = maskLayer;

Effect picture:


Personally, I think the second solution is simpler and has stronger functional scalability.

Setting up shadows

Conventional practice:

  1. //Shadow color
  2. self.imageView.layer.shadowColor= [UIColorblackColor].CGColor;
  3. // Transparency of the shadow
  4. self.imageView.layer.shadowOpacity=0.8f;
  5. //Shadow rounded corners
  6. self.imageView.layer.shadowRadius=4;
  7. //Shadow offset
  8. self.imageView.layer.shadowOffset=CGSizeMake(0,0);  

Optimization plan:

Avoid modifying shadowOffset directly. Instead, call setShadowPath to provide a CGPath to the view's layer and provide the shape of the rendered View to Core Animation, which will reduce off-screen rendering calculations.

  1. [self.imageView.layer setShadowPath:[[UIBezierPath
  2. bezierPathWithRect:myView.bounds] CGPath]];

Supplement: When the shape of the view using the shadow changes, the shadowPath does not change with the bounds property of CALayer, so after the bounds of the layer change, you need to manually update the shadowPath to make it adapt to the new bounds.

<<:  WeChat ID can be changed at will? Official: It's just a bug

>>:  Are mobile programmers having an easy time in 2017?

Recommend

Apple launches iOS 10.2 Beta2. What are the new changes?

Recently, Apple pushed iOS 10.2 Beta 2 to develop...

Kuaishou short video product analysis!

Short videos have become a part of our daily live...

Jack Trading Academy JTA: Harami Trading System

Jack Trading Academy JTA: Introduction to the res...

New Media Operation Writing Guide!

What should a novice do if his writing is rejecte...

Is sweating more really good for your health?

The weather is getting hotter and hotter. I sweat...

Pinduoduo: Building a growth model with the North Star indicator

The North Star refers to the absolute core indica...

How to attract traffic on Weibo? Precise traffic generation techniques on Weibo!

In this era, many people are scrambling to gather...

Why do airplane meals taste so bad? It may not be the food that’s to blame

When you are on an airplane, you may have had thi...

Why did LeTV Super TV win the Double 11 championship for three consecutive times?

On November 11, 2014, LeTV Super TV’s total sales...