IOS development diary 52-CALayer and coreAnimation, calayeranimation

Source: Internet
Author: User

IOS development diary 52-CALayer and coreAnimation, calayeranimation

Today, the blogger has a demand for CALayer and coreAnimation, and has encountered some difficulties. I would like to share with you the hope to make common progress.

General Practice of progress bars

The progress bar does not simply grow linearly. Before 50%, each time the progress bar increases, the progress bar will offset a distance above the Y axis until it reaches the vertex at the offset position when it increases to half of the progress, then, as the progress continues to increase, the y-axis offset becomes smaller and smaller until it returns to a straight line.
From the implementation perspective, useCAShapeLayerAnd then update it each time the progress changes.pathValue. If you useCAShapeLayerIn this way, we need to create two instance objects. One is placed below as the background of the progress bar, and the other is changed as the progress changes. The figure is as follows:


Every time the progress changes, we need to calculate the progress coordinate position based on the current progress, and then updatepathThe Code is as follows:

- (void)updatePath{    UIBezierPath * path = [UIBezierPath bezierPath];    [path moveToPoint: CGPointMake(25, 150)];    [path addLineToPoint: CGPointMake((CGRectGetWidth([UIScreen mainScreen].bounds) - 50) * _progress + 25, 150 + (25.f * (1 - fabs(_progress - 0.5) * 2)))];    [path addLineToPoint: CGPointMake(CGRectGetWidth([UIScreen mainScreen].bounds) - 25, 150)];    self.background.path = path.CGPath;    self.top.path = path.CGPath;    self.top.strokeEnd = _progress;}

In fact, when we use this method to achieve the progress effect, the progress will be slower than the response drawn directly in the current context, that is, we can see the effect of this delayed update with the naked eye, is not conducive to user experience. Second, we need to create an additional background layer, with additional costs in memory.

Custom layer

In this section, we need to customizeCALayerTo achieve the above progress bar effect, we need to open the progress attribute. Every time this value changes, we need to call[self setNeedsDisplay]To redraw the progress bar

@property(nonatomic, assign) CGFloat progress;

Override the setter method to check the progress value range and redraw the progress bar.

- (void)setProgress: (CGFloat)progress{    _progress = MIN(1.f, MAX(0.f, progress));    [self setNeedsDisplay];}

Review the progress bar. We can divide the progress bar into two lines: the green finished progress bar and the gray progress bar. Depending on the progress bar, there are three states: <0.5, = 0.5,> 0.5:


We can see that when the Progress reaches half, the offset of our progress bar on the Y axis reaches the maximum value. Therefore, we should define a MAX_OFFSET value.

#define MAX_OFFSET 25.f

On the other hand, the y-axis offset of the current progress bar is offset proportionally according to the progress. When we change progress _ progress, redraw the progress bar. Below is the green progress bar

- (void)drawInContext: (CGContextRef)ctx{    CGFloat offsetX = _origin.x + MAX_LENGTH * _progress;    CGFloat offsetY = _origin.y + _maxOffset * (1 - fabs((_progress - 0.5f) * 2));    CGMutablePathRef mPath = CGPathCreateMutable();    CGPathMoveToPoint(mPath, NULL, _origin.x, _origin.y);    CGPathAddLineToPoint(mPath, NULL, offsetX, offsetY);    CGContextAddPath(ctx, mPath);    CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);    CGContextSetLineWidth(ctx, 5.f);    CGContextSetLineCap(ctx, kCGLineCapRound);    CGContextStrokePath(ctx);    CGPathRelease(mPath);}

Ps:There is a very important problem here. The custom layer must be added to our custom view to achieve drawInContext: The method is constantly repainted. For more use of coreGraphics related methods, refer to this article.

The gray lines in the second part are drawn based on the coordinates of the current offset. There are two traps:

  • Unskilled developers can easily put the code for drawing gray lines behind the code above. This will cause the gray lines to be drawn behind the green lines, and the green lines to cover some of them so that the end of the green lines are not circular.
  • The value of _ progress is not determined. When _ progress is 0, the above Code also generates a green dot on the left side of the line, which is inaccurate.

Therefore, when determining the offset coordinates corresponding to the current progress, we should draw a gray line and then draw a green progress bar. Make a judgment on _ progress before drawing a green line

- (void)drawInContext: (CGContextRef)ctx{    CGFloat offsetX = _origin.x + MAX_LENGTH * _progress;    CGFloat offsetY = _origin.y + _maxOffset * (1 - fabs((_progress - 0.5f) * 2));    CGMutablePathRef mPath = CGPathCreateMutable();    CGPathMoveToPoint(mPath, NULL, offsetX, offsetY);    CGPathAddLineToPoint(mPath, NULL, _origin.x + MAX_LENGTH, _origin.y);    CGContextAddPath(ctx, mPath);    CGContextSetStrokeColorWithColor(ctx, [UIColor lightGrayColor].CGColor);    CGContextSetLineWidth(ctx, 5.f);    CGContextSetLineCap(ctx, kCGLineCapRound);    CGContextStrokePath(ctx);    CGPathRelease(mPath);    if (_progress != 0.f) {        mPath = CGPathCreateMutable();        CGPathMoveToPoint(mPath, NULL, _origin.x, _origin.y);        CGPathAddLineToPoint(mPath, NULL, offsetX, offsetY);        CGContextAddPath(ctx, mPath);        CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);        CGContextSetLineWidth(ctx, 5.f);        CGContextSetLineCap(ctx, kCGLineCapRound);        CGContextStrokePath(ctx);        CGPathRelease(mPath);    }}

At this time, add a UISlider drag in the controller to control the progress of your progress bar and see if the desired effect is complete.

Extension

In the above plot, the color of the fill color is written to death, which is not conducive to code expansion. Looking back at CAShapeLayer, we added similar attributes such as fillColor and strokeColor Based on the Inheritance of CALayer. We can add similar member attributes for encapsulation. here we need to add two attributes for the progress bar, the progress bar color and background color respectively.

@property(nonatomic, assign) CGColorRef backgroundColor;@property(nonatomic, assign) CGColorRef strokeColor;

When we set the color, we can directly input color. CGColor to complete the assignment. We will change the above set color code to the following code and then re-run it.

CGContextSetStrokeColorWithColor(ctx, _backgroundColor);CGContextSetStrokeColorWithColor(ctx, _strokeColor);

Some of my friends will find something wrong. It crashes and appears.EXC_BAD_ACCESSError -- if you use[UIColor xxxColor].CGColorSo there is no problem here.
This is because the two attributes we added are of the assign type. When we use this color, it has been released for a long time. Here we can see two things:

  • CAShapeLayer bridges or references non-object attributes that belong to coreGraphics.
  • [UIColor xxxColor] The object returned by the method should be a global or static object. To save memory consumption, the lazy loading method should be used. If necessary, you can optimize the memory by not calling these methods.

Therefore, we should rewrite the setter method of these two attributes to implement reference (Welcome to MRC)

- (void)setStrokeColor: (CGColorRef)strokeColor{    CGColorRelease(_strokeColor);    _strokeColor = strokeColor;    CGColorRetain(_strokeColor);    [self setNeedsDisplay];}

In addition, CAShapeLayer has an interesting property.strokeEndThis attribute determines how many parts of the entire layer need to be rendered. The audience who want to check this attribute can set this attribute for the layer in the general code at the beginning. Then, you will find that no matter how many progress pages we set, the green part of the progress bar is always equivalentstrokeEnd. Shows the effect.


As you can see, it is more difficult to draw the interface based on strokeEnd. But we can also split this into two situations:
1. strokeEnd> progress
This situation corresponds to the above two figures. Of course, the status of progress = 1 is the same as that of progress = 0. The progress bar is divided into three parts:

  • Green line on the left of the offset point
  • Green lines on the right
  • Last Gray Line

The y coordinate of the junction point is divided by the percentage of the strokeEnd exceeding the progress by the percentage of the current total length on the right to the total length of the Line, as shown in


Therefore, we need to determine two coordinate points. The offset points are obtained according to the above Code according to progress, and the Code for calculating the background color and progress color handover points is as follows:

CGFloat contactX = _origin.x + MAX_LENGTH * _strokeEnd;CGFloat contactY = _origin.y + (offsetY - _origin.y) * ((1 - (_strokeEnd - _progress) / (1 - _progress)));

2. strokeEnd <= progress
At this time, it corresponds to the following two figures. Similarly, we can split the progress bar into three parts:

  • Green progress bar on the left
  • The background color bar between the progress bar and the offset point.
  • Background color bar on the right

The above graphic analysis is equivalent to placing the location information on the right to the left. We can easily calculate the coordinates of the color handover points.

CGFloat contactX = _origin.x + MAX_LENGTH * _strokeEnd;CGFloat contactY = (offsetY - _origin.y) * (_progress == 0 ?: _strokeEnd / _progress) + _origin.y;

With the above resolution calculation,drawInContextThe Code is as follows:

-(Void) drawInContext: (CGContextRef) ctx {CGFloat offsetX = _ origin. x + MAX_LENGTH * _ progress; CGFloat offsetY = _ origin. y + _ maxOffset * (1-fabs (_ progress-0.5f) * 2); CGFloat contactX = 25.f + MAX_LENGTH * _ strokeEnd; CGFloat contactY = _ origin. y + _ maxOffset * (1-fabs (_ strokeEnd-0.5f) * 2); CGRect textRect = CGRectOffset (_ textRect, MAX_LENGTH * _ progress, _ maxOffset * (1-fabs (_ progress -0.5f) * 2); if (_ report) {_ report (NSUInteger) (_ progress * 100), textRect, _ strokeColor );} CGMutablePathRef linePath = CGPathCreateMutable (); // draw the background line if (_ strokeEnd> _ progress) {CGFloat scale = _ progress = 0?: (1-(_ strokeEnd-_ progress)/(1-_ progress); contactY = _ origin. y + (offsetY-_ origin. y) * scale; CGPathMoveToPoint (linePath, NULL, contactX, contactY);} else {CGFloat scale = _ progress = 0?: _ StrokeEnd/_ progress; contactY = (offsetY-_ origin. y) * scale + _ origin. y; CGPathMoveToPoint (linePath, NULL, contactX, contactY); CGPathAddLineToPoint (linePath, NULL, offsetX, offsetY);} CGPathAddLineToPoint (linePath, NULL, _ origin. x + MAX_LENGTH, _ origin. y); [self setPath: linePath onContext: ctx color: [UIColor colorWithRed: 204/255. f green: 204/255. f blue: 204/255. f alpha: 1.f]. CGColor]; C GPathRelease (linePath); linePath = CGPathCreateMutable (); // draw progress line if (_ progress! = 0.f) {CGPathMoveToPoint (linePath, NULL, _ origin. x, _ origin. y); if (_ strokeEnd> _ progress) {CGPathAddLineToPoint (linePath, NULL, offsetX, offsetY);} CGPathAddLineToPoint (linePath, NULL, contactX, contactY );} else {if (_ strokeEnd! = 1.f & _ strokeEnd! = 0.f) {CGPathMoveToPoint (linePath, NULL, _ origin. x, _ origin. y); CGPathAddLineToPoint (linePath, NULL, contactX, contactY);} [self setPath: linePath onContext: ctx color: [UIColor colorWithRed: 66/255. f green: 1.f blue: 66/255. f alpha: 1.f]. CGColor]; CGPathRelease (linePath );}

We encapsulate the code for adding CGPathRef and setting parameters such as line color and sizesetPath: onContext: colorTo reduce the amount of code.

 

Http://www.cocoachina.com/ios/20141022/10005.html? 1413946650

Http://www.cnblogs.com/wendingding/p/3800736.html

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.