iOS development diary 52-calayer and Coreanimation

Source: Internet
Author: User

Today, Bo Master has a calayer and coreanimation demand, met some difficulties, here and we share, hope to progress together.

Progress bar General Practice

Progress bar is not purely linear growth, before 50%, each time the progress, the progress bar will be on the y-axis offset a distance, until the increase to half the progress of the offset position to reach the vertex, and as the progress continues to increase, the y-axis offset is getting smaller, until the return to a straight line.
From an implementation perspective, it CAShapeLayer can be achieved by updating its value every time the progress is changed path . If you use CAShapeLayer the way, we need to create two instance objects, one placed below as the background of the progress bar, and the other on the top as the progress changes. This is illustrated below:


Each time the progress changes, we have to calculate the progress coordinate position according to the current progress, and then update two layers path , the code is as follows:

- (void) updatepath{Uibezierpath * Path = [Uibezierpath Bezierpath]; [Path moveToPoint:Cgpointmake (25,150)]; [Path Addlinetopoint:Cgpointmake ((Cgrectgetwidth ([UIScreen Mainscreen]. Bounds)-* _progress +25,150 + (25.f * (1-fabs (_progress- Span class= "Hljs-number" >0.5) * 2))]; [Path addlinetopoint: cgpointmake (cgrectgetwidth ([uiscreen mainscreen].bounds)-25, Span class= "Hljs-number" >150)]; self.background.path = path< Span class= "hljs-variable". Cgpath; self.top.path = Pathself.top.strokeEnd = _ Progress;}                

In fact, when using this approach to achieve progress, the progress will be slower than directly in the current context to draw the response to a few frames, that is, we can see this delay update of the effect, is detrimental to the user experience. Second, we need to create an extra background layer, which has extra cost in memory.

Custom Layer

In this section we want to CALayer implement the above progress bar effect through a custom subclass, we need to open the Progress property. Every time this value changes, we're going to call to redraw the [self setNeedsDisplay] progress bar.

@property(nonatomic, assign) CGFloat progress;

Overriding setter methods, detecting the range of progress values, and redrawing the progress bar

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

To revisit the progress bar, we can divide the progress bar into two lines, namely the Green completed progress bar and the gray progress bar. According to the different progress bar, divided into <0.5, =0.5, >0.5 Three kinds of states:


As we know, the offset of our progress bar on the Y axis reaches the maximum at the halfway point of the progress. Therefore, we should define a maximum offset value of max_offset.

#define MAX_OFFSET 25.f

On the other hand, the y-axis offset of the current progress bar is offset proportionally to the progress. Redraw the progress bar as we change the progress _progress. The following is the drawing of 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]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 above, in order to achieve Drawincontext: method of continuous redrawing. For more use of coregraphics related methods, please refer to this article

The second part of the Gray Line is drawn based on the coordinates of the current offset, where there are two small traps:

    • It is easy for unskilled developers to put code that draws gray lines directly behind the code above. This causes the gray lines to draw behind the green lines and cover the green lines so that the end of the Green Line is not round.
    • The value of the _progress is not judged. When _progress is 0 o'clock, the above code also generates a small green dot on the left side of the line, which is inaccurate.

Therefore, when we determine the offset coordinates of the current progress, we should draw the Gray line directly and then draw the green progress bar. _progress should be judged before drawing the 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]cgcontextsetlinewidth (CTX, 5.f); cgcontextsetlinecap (CTX, Kcglinecapround); cgcontextstrokepath (CTX); cgpathrelease (MPath);}}          

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

Extended

Above we in the implementation of the drawing, the color of the fill color is written dead, this is not conducive to code extension. Recalling Cashapelayer, we add similar properties such as FillColor, Strokecolor, and so on, on the basis of inheriting calayer, we can complete the encapsulation by adding similar member properties, here we need to add two properties to the progress bar, Indicates the progress bar color and background color, respectively

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

We pass in color directly when we set the colors. Cgcolor will be able to complete the assignment, we have to change the above set color code as shown below and then rerun

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

Some friends will find a pit-daddy thing, Crash, EXC_BAD_ACCESS error-If you use the system provided [UIColor xxxColor].CGColor , then there is no problem.
This is because we have added two properties to the assign type, which is already released when we use this color. From here we can see two things:

    • Cashapelayer will bridge or reference a property that is not an object and belongs to Coregraphics
    • The object returned by the [Uicolor Xxxcolor] method should be a global or static object. In order to save memory consumption, you should use lazy loading mode. If necessary, you can not call these methods to achieve the effect of optimizing memory

Therefore, we should override the setter method of both properties to implement the reference (Welcome to MRC)

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

In addition, Cashapelayer has an interesting property strokeEnd that determines how much of the entire layer needs to be rendered. Crossing who want to see this property can set this property for the layer in the first regular code, and then you'll find that the green part of the progress bar is always the same regardless of how much of our progress is set strokeEnd . The effect is as shown


As you can see, the difficulty of drawing the interface is more complicated when drawing based on Strokeend. But we can also split this up into two different situations.
1, Strokeend>progress
This situation corresponds to the above two graphs, of course, in the Progress=1 and progress=0 state is the same. As you can see, when the progress is not zero, the progress bar is divided into three parts:

    • Green Line to the left of the offset point
    • Green lines on the right
    • The last gray Line

The y-coordinate of the junction point should be the percentage portion of the total length of the line divided by strokeend beyond progress, as shown in the current right.


So we need to judge two coordinate points, where the offset points are based on progress as shown above, and the code for calculating the background color and progress color junction 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
This time it corresponds to the following two graphs, the same, we can split the progress bar into three parts:

    • Green progress bar on the far left
    • Background color bar in the middle of the progress bar and offset point
    • Background color bar on right

According to the above graphical analysis, equivalent to the right side of the position information on the left, we can easily get the color junction point coordinate calculation method

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

With the above analytic calculation, drawInContext the 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 *(+), Textrect, _strokecolor); } cgmutablepathref Linepath = Cgpathcreatemutable ();Draw a background lineif (_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]; Cgpathrelease (Linepath); Linepath = Cgpathcreatemutable ();Draw a progress lineif (_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 reduce the amount of code by encapsulating code that adds cgpathref and sets parameters such as line color, size, and so on setPath: onContext: color .

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

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

iOS development diary 52-calayer and Coreanimation

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.