Mobile Access is poor, please visit my personal blog
Recently, a pull up to refresh the small wheel, as long as the following a protocol can customize their own dynamic pull-up refresh and load, I also wrote a few of the action, the following is a better implementation of the dynamic effect of the process
First up and GitHub address, there are other good dynamic effects can also be exchanged ~
Dynamic effect of the original address, on the Uimovement website to see the dynamic effect when the feeling special 6, I want to realize their own, a long time, for a number of programs finally realized, the following are the steps to achieve:
Analysis of dynamic effects
Write a dynamic effect of the first step should be careful to analyze it, each frame of it to see, find a most appropriate way to achieve it, the following is my analysis process:
- See the curve, will certainly think
CAShapeLayer
and UIBezierPath
this pair of partners, relative to CoreGraphics
, it is simple and efficient;
- The pull effect of the curve can be
CADisplayLink
combined with a reference view
, a reference view
for UIBezierPath
a controlPoint
, mobile reference view
to achieve the effect of the curve pull;
- The rebound effect of the curve is considered repeatedly after the decision to use
CAKeyframeAnimation
CAShapeLayer
the match to use, originally intended CASpringanimation
to use to achieve, but consider it is iOS9
out, and my wheel minimum support iOS8, give up with it;
- Small ball is realized and pop-up is relatively simple, use
CAShapeLayer
to achieve small ball, use CABasicAnimation
to achieve the movement of small ball;
- The effect of the rotation of the outer ring of the ball, the first is
CAShapeLayer
to achieve the ring, and then with CABasicAnimation
CAShapeLayer
the control strokeEnd
and transform.rotation.z
always to achieve the effect of the outer ring rotation;
- Finally is more complex is the ball and the curve of the connection between the processing, my implementation is through the process of the
CADisplayLink
animation in real-time to listen to the position of the ball and curve, calculate the UIBezierPath
use CAShapeLayer
of a precise connection between the ball and the curve part.
Well, the above is the approximate process, if you have another better way to achieve, can also be discussed together.
Draw the curve and pull the curve
We use CAShapeLayer
and UIBezierPath
this pair of partners to achieve the drawing of the curve, the following with a reference to show view
you, the following is the main code and:
By passing theyCoordinates to draw the curve Func wave (_y: CGFloat, execute:cgfloat) {self. Execute= Execute Wavelayer. Path= Wavepath (x:0,y:y) if!isanimation {var trans = Cgaffinetransform. Identitytrans = trans. Translatedby(x:0,y:y) reference. Transform= Trans}}//calculates pathprivate func Wavepath (x: CGFloat,y: cgfloat), Cgpath {Let w = frame. WidthLet path = Uibezierpath () ify< Execute {path. Move(To:. Zero) path. AddLine(To:. Init(x: W,y:0)) path. AddLine(To:. Init(x: W,y:y)) path. AddLine(To:. Init(x:0,y:y)) path. AddLine(To:. Zero)}else {Path. Move(To:. Zero) path. AddLine(To:. Init(x: W,y:0)) path. AddLine(To:. Init(x: W,y: Execute)) path. Addquadcurve(To:. Init(x:0,y: Execute), ControlPoint:. Init(x: w/2,y:y)) path. AddLine(To:. Zero)} return path. Cgpath}
Springback Effect of Curves
The springback of the curve is CAKeyframeAnimation
added to the reference, then the coordinates of the monitoring reference are used to view
CADisplayLink
view
controlPoint
Achieve the rebound effect of the curve, and the following is the main code and:
Start animation func startanimation () {isanimation = True Adddisplay () boundanimation (x:0,y: Execute)}//cakeyframeanimation animation private func boundanimation (x: CGFloat,y: cgfloat) {Let bounce = cakeyframeanimation (keypath:"TRANSFORM.TRANSLATION.Y") Bounce. Timingfunction= Camediatimingfunction (Name:kcamediatimingfunctioneasein) Bounce. Duration= Bounceduration Bounce. Values= [Reference. Frame. Origin. Y,y*0.5,y*1.2,y*0.8,y*1.1,y] Bounce. Isremovedoncompletion= True Bounce. Fillmode= Kcafillmodeforwards Bounce. Delegate= self reference. Layer. Add(Bounce, Forkey:"Return")}//Add and remove cadisplaylinkprivate func adddisplay () {displayLink = Cadisplaylink (target:self, selector:#selector (displayaction))DisplayLink?. Add(To:. Main, Formode:. Commonmodes)}private func Removedisplay () {DisplayLink?. Invalidate() DisplayLink = nil}//Cadisplaylink bound method @objc private func displayaction () {if let frame = reference. Layer. Presentation()?. Frame{Dispatchqueue. Global(). Async{Let path = self. Displaywavepath(x:0,y: Frame. Origin. Y+ referenceheight/2) Dispatchqueue. Main. Async{Self. Wavelayer. Path= path}}}}//This method gets the pathprivate func displaywavepath (x: CGFloat,y: cgfloat), Cgpath {Let w = frame. WidthLet path = Uibezierpath () path. Move(To:. Zero) path. AddLine(To:. Init(x: W,y:0)) path. AddLine(To:. Init(x: W,y: Execute)) path. Addquadcurve(To:. Init(x:0,y: Execute), ControlPoint:. Init(x: w/2,y:y)) path. AddLine(To:. Zero) Return path. Cgpath}
Animation of the outer ring
Ball and outer ring we use CAShapeLayer
to draw, here is mainly about the implementation of animation, animation is mainly composed of two parts:
CABasicAnimation
The animation that controls the outer ring strokeEnd
;
CABasicAnimation
Controls the rotation animation of the outer ring transform.rotation.z
;
strokeEnd Animation | of the outer ring's animated
outer ring transform.rotation.z rotation |
|
|
Here is the key code:
Func animation () {Self. Ishidden= False Let rotate = cabasicanimation (keypath:"Transform.rotation.z") Rotate. Fromvalue=0Rotate. Tovalue= M_pi *2Rotate. Duration=1Rotate. Timingfunction= Camediatimingfunction (name:kcamediatimingfunctionlinear) rotate. RepeatCount= HUGE Rotate. Fillmode= Kcafillmodeforwards Rotate. Isremovedoncompletion= False Self. Add(Rotate, Forkey:rotate. KeyPath) strokeendanimation ()}func strokeendanimation () {Let endPoint = Cabasicanimation (keypath:"Strokeend") EndPoint. Fromvalue=0EndPoint. Tovalue=1EndPoint. Duration=1.8EndPoint. Timingfunction= Camediatimingfunction (name:kcamediatimingfunctioneaseout) endPoint. RepeatCount= HUGE EndPoint. Fillmode= Kcafillmodeforwards EndPoint. Isremovedoncompletion= False EndPoint. Delegate= SelfAdd(EndPoint, Forkey:endpoint. KeyPath)}
The ball rising and the handling at the junction
Small ball rise animation is very simple, an CABasicAnimation
animation on the implementation, the main trouble is the connection at the animation implementation, my plan is in the small ball animation process through CADisplayLink
real-time monitoring of the ball and reference view
location, calculate the Besle curve, and then through a named linkLayer: CAShapeLayer
layer
to connect them , and then let them disconnect in a specific place, following the main code and:
@objc private func displayaction () {Let Offy = Balllayer. Circlelayer. Presentation()?. Frame. Origin. YLet frame1 = Balllayer. FrameLet frame2 = Wavelayer. Reference. Layer. Presentation()?. FrameIf let Offy = Offy, let frame2 = frame2 {dispatchqueue. Global(). Async{//To determine if the ball is up or down, false is on, speed is fast, get position not in time, downward need to adjust position let Isincrement = (offy-self. Previousoffy) >0Let path = Uibezierpath () Let X1 = frame1. Origin. x+ (isincrement?)4:0) Let Y1 = frame1. Origin. Y+ offy Let w1 = frame1. Size. Width-(isincrement?)8:0) Let H1 = Frame1. Size. HeightLet x2 = frame2. Origin. xLet y2 = frame2. Origin. YLet W2 = frame2. Size. WidthLet H2 = Frame2. Size. HeightLet Suby = y2-y1//y1 and y2 spacing let subscale = suby/self. Execute/2The distance to be disconnected isTenLet executesub = self. Balllayer. Circlelayer. Moveupdist+ Offy If executesub <Ten{if!isincrement {let Executesubscale = executesub/TenPath. Move(To:. Init(x: X1- the,y: Y2 + h2/2+ the)) path. AddLine(To:. Init(x: X1 + W1 + the,y: Y2 + h2/2+ the)) path. Addquadcurve(To:. Init(x: X1- the,y: Y2 + h2/2+ the), ControlPoint:. Init(x: X1 + w1/2,y: Y2 + h2/2-Self. Execute/6* Executesubscale)}}else {Path. Move(To:. Init(x: X2,y: y2 + H2)) path. AddLine(To:. Init(x: X2 + W2,y: y2 + H2)) path. Addquadcurve(To:. Init(x: X1 + W1,y: Y1 + h1/2), ControlPoint:. Init(x: X1 + w1-w1*2*subscale,y: Y1 + (Y2-Y1)/2+ h1/2+ h2/2)) path. AddLine(To:. Init(x: x1,y: Y1 + h1/2)) path. Addquadcurve(To:. Init(x: X2,y: y2 + H2), ControlPoint:. Init(x: X1 + w1*2*subscale,y: Y1 + (Y2-Y1)/2+ h1/2+ h2/2) if Y1 + h1 <= Self. Execute, isincrement {Dispatchqueue. Main. Async{Self. Wavelayer. Startdownanimation()}}} Dispatchqueue. Main. Async{Self. Linklayer. Path= Path. Cgpath} Self. Previousoffy= Offy}}}
I think this place is not very good treatment, but the simple and rough solve the problem, if you have better suggestions, you can put forward, we exchange learning ~
Complete code, you can go to GitHub address to download, welcome to star and comments and contribution code, there are good dynamic words can also provide, finally thank you for reading
iOS animation advanced-Cool pull-up refresh action