How To Create Loading Animation Ios Swift
With more i.4 one thousand thousand apps in the iOS App Store today, it'due south a existent challenge to make your app stand out. You have a very small window of opportunity to capture the attending of your users before your app ends up in the big black pigsty of obscurity.
In that location'southward no amend place to showtime wowing your users than at the loading screen of your app, where you can add a delightful animation that serves as a forerunner to your on-boarding or authentication workflow.
In this tutorial you volition learn how to brand such an animation. You'll learn how to build it up piece-by-slice, utilising avant-garde techniques to create a fluid and captivating animation.
Getting Started
Download the starter projection for this tutorial here, salvage it to a user-friendly location and open it in Xcode.
Open up HolderView.swift. In this UIView
subclass, yous will add and breathing the following sublayers (constitute in the Layers subgroup) as shown in the animation in a higher place:
- OvalLayer.swift: This is the beginning layer, which expands from nothing size and so wobbles for a brusk menses of time.
- TriangleLayer.swift: This next layer appears while the
OvalLayer
is wobbling. When this view rotates,OvalLayer
contracts dorsum to zero size leaving just theTriangleLayer
visible. - RectangleLayer.swift: This layer serves as a visual container of sorts for the
TriangleLayer
. - ArcLayer.swift: This layer fills the
RectangleLayer
with an animation result that'due south very like to a drinking glass being filled with water.
Open up OvalLayer.swift; the starter project already contains the code to initialize this layer and all the Bezier paths you'll use in your animations. You lot'll see that expand()
, wobble()
and contract()
are all empty; y'all'll populate those methods equally you work through the tutorial. All the other *Layer files are structured in a similar fashion.
Finally, open ViewController.swift and take a look at addHolderView()
; this method adds an example of HolderView
as a subview to the center of the view controller'south view. This view will house all the animations. The view controller just needs to put information technology on the screen, and the view will take care of the actual blitheness lawmaking.
The animateLabel()
function is a delegate callback provided by the HolderView
class that you will fill in every bit you lot consummate the animation sequence. addButton()
merely adds a button to the view so that you can tap and restart the animation.
Build and run your app; you should come across an empty white screen. An empty canvas — the perfect thing on which to start creating your new animations! :]
By the end of this tutorial, your app will look like this:
And so without further ado, let'south get started!
Adding The Oval
The animation starts with a red oval that expands into view from the centre of the screen and then wobbles effectually a bit.
Open HolderView.swift and declare the following abiding near the top of the HolderView
course:
let ovalLayer = OvalLayer()
Now add together the post-obit function to the bottom of the class:
func addOval() { layer.addSublayer(ovalLayer) ovalLayer.expand() }
This first adds the OvalLayer
instance you created above equally a sublayer to the view's layer, then calls expand()
, which is one of the stubbed-out functions you lot need to fill in.
Go to OvalLayer.swift and add the post-obit code to aggrandize()
:
func expand() { var expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path") expandAnimation.fromValue = ovalPathSmall.CGPath expandAnimation.toValue = ovalPathLarge.CGPath expandAnimation.duration = animationDuration expandAnimation.fillMode = kCAFillModeForwards expandAnimation.removedOnCompletion = false addAnimation(expandAnimation, forKey: nil) }
This role creates an example of CABasicAnimation
that changes the oval's path from ovalPathSmall
to ovalPathLarge
. The starter project provides both of these Bezier paths for you. Setting removedOnCompletion
to fake
and fillMode
to KCAFillModeForwards
on the animation lets the oval retain its new path once the blitheness has finished.
Finally, open ViewController.swift and add the following line to addHolderView()
just below view.addSubview(holderView)
:
holderView.addOval()
This calls addOval
to kickstart the animation after it has been added to the view controller'due south view.
Build and run your app; your animation should now await like this:
Wobbling The Oval
With your oval at present expanding into view, the next step is to put some bounce in its pace and make information technology wobble.
Open up HolderView.swift and add together the following part to the bottom of the class:
func wobbleOval() { ovalLayer.wobble() }
This calls the stubbed-out method wobble()
in OvalLayer
.
At present open OvalLayer.swift and add the post-obit code to wobble()
:
func wobble() { // i var wobbleAnimation1: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation1.fromValue = ovalPathLarge.CGPath wobbleAnimation1.toValue = ovalPathSquishVertical.CGPath wobbleAnimation1.beginTime = 0.0 wobbleAnimation1.duration = animationDuration // 2 var wobbleAnimation2: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation2.fromValue = ovalPathSquishVertical.CGPath wobbleAnimation2.toValue = ovalPathSquishHorizontal.CGPath wobbleAnimation2.beginTime = wobbleAnimation1.beginTime + wobbleAnimation1.duration wobbleAnimation2.duration = animationDuration // 3 var wobbleAnimation3: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation3.fromValue = ovalPathSquishHorizontal.CGPath wobbleAnimation3.toValue = ovalPathSquishVertical.CGPath wobbleAnimation3.beginTime = wobbleAnimation2.beginTime + wobbleAnimation2.duration wobbleAnimation3.duration = animationDuration // 4 var wobbleAnimation4: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation4.fromValue = ovalPathSquishVertical.CGPath wobbleAnimation4.toValue = ovalPathLarge.CGPath wobbleAnimation4.beginTime = wobbleAnimation3.beginTime + wobbleAnimation3.duration wobbleAnimation4.elapsing = animationDuration // 5 var wobbleAnimationGroup: CAAnimationGroup = CAAnimationGroup() wobbleAnimationGroup.animations = [wobbleAnimation1, wobbleAnimation2, wobbleAnimation3, wobbleAnimation4] wobbleAnimationGroup.duration = wobbleAnimation4.beginTime + wobbleAnimation4.duration wobbleAnimationGroup.repeatCount = 2 addAnimation(wobbleAnimationGroup, forKey: zero) }
That'southward a lot of code, just information technology breaks down nicely. Here's what's going on:
- Animate from the big path down to being squished vertically.
- Modify from a vertical squish to squished both horizontally and vertically.
- Swap back to vertical squish.
- Cease the blitheness, ending back at the large path.
- Combine all of your animations into a
CAAnimationGroup
and add together this grouping animation to yourOvalLayout
.
The beginTime
of each subsequent animation is the sum of the beginTime
of the previous animation and its duration
. You echo the animation group twice to give the wobble a slightly elongated feel.
Even though you now have all the code required to produce the wobble animation, you aren't calling your new animation nonetheless.
Go dorsum to HolderView.swift and add together the following line to the finish of addOval()
:
NSTimer.scheduledTimerWithTimeInterval(0.iii, target: self, selector: "wobbleOval", userInfo: nix, repeats: false)
Here y'all create a timer that calls wobbleOval()
correct after the OvalLayer
has finished expanding.
Build and run your app; check out your new animation:
It's very subtle, only that'southward an important factor of a truly delightful animation. Y'all don't demand things to be flying all over the screen!
Get-go The Morph
It's time to get a footling fancy! :] You're going to morph the oval into a triangle. To the user'due south centre, this transition should look completely seamless. Yous'll use ii split up shapes of the same colour to brand this work.
Open HolderView.swift and add the following lawmaking to the top of HolderView
class, just below the ovalLayer
property y'all added earlier:
let triangleLayer = TriangleLayer()
This declares a constant case of TriangleLayer
, just like yous did for OvalLayer
.
At present, make wobbleOval()
expect like this:
func wobbleOval() { // ane layer.addSublayer(triangleLayer) // Add this line ovalLayer.wobble() // 2 // Add the code below NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: "drawAnimatedTriangle", userInfo: cypher, repeats: false) }
The code to a higher place does the post-obit:
- This line adds the
TriangleLayer
example y'all initialized earlier as a sublayer to theHolderView
's layer. - Since you know that the wobble animation runs twice for a full duration of
1.8
, the half-fashion indicate would exist a bang-up place to start the morphing process. You therefore add together a timer that addsdrawAnimatedTriangle()
afterward a delay of0.9
.
Note: Finding the correct duration or delay for animations takes some trial and error, and can mean the divergence between a practiced animation and a fantastic one. I encourage you to tinker with your animations to get them looking perfect. It can take some time, but it'southward worth it!
Adjacent, add the following role to the lesser of the class:
func drawAnimatedTriangle() { triangleLayer.breathing() }
This method is called from the timer that you lot just added to wobbleOval()
. It calls the (currently stubbed out) method in triangleLayer
which causes the triangle to breathing.
At present open TriangleLayer.swift and add the following code to breathing()
:
func animate() { var triangleAnimationLeft: CABasicAnimation = CABasicAnimation(keyPath: "path") triangleAnimationLeft.fromValue = trianglePathSmall.CGPath triangleAnimationLeft.toValue = trianglePathLeftExtension.CGPath triangleAnimationLeft.beginTime = 0.0 triangleAnimationLeft.duration = 0.iii var triangleAnimationRight: CABasicAnimation = CABasicAnimation(keyPath: "path") triangleAnimationRight.fromValue = trianglePathLeftExtension.CGPath triangleAnimationRight.toValue = trianglePathRightExtension.CGPath triangleAnimationRight.beginTime = triangleAnimationLeft.beginTime + triangleAnimationLeft.duration triangleAnimationRight.duration = 0.25 var triangleAnimationTop: CABasicAnimation = CABasicAnimation(keyPath: "path") triangleAnimationTop.fromValue = trianglePathRightExtension.CGPath triangleAnimationTop.toValue = trianglePathTopExtension.CGPath triangleAnimationTop.beginTime = triangleAnimationRight.beginTime + triangleAnimationRight.elapsing triangleAnimationTop.duration = 0.xx var triangleAnimationGroup: CAAnimationGroup = CAAnimationGroup() triangleAnimationGroup.animations = [triangleAnimationLeft, triangleAnimationRight, triangleAnimationTop] triangleAnimationGroup.duration = triangleAnimationTop.beginTime + triangleAnimationTop.duration triangleAnimationGroup.fillMode = kCAFillModeForwards triangleAnimationGroup.removedOnCompletion = false addAnimation(triangleAnimationGroup, forKey: nil) }
This code animates the corners of TriangleLayer
to pop out one-by-one every bit the OvalLayer
wobbles; the Bezier paths are already defined for each corner as part of the starter project. The left corner goes first, followed by the right and and so the top. You do this past creating iii instances of a path-based CABasicAnimation
that you add to a CAAnimationGroup
, which, in turn, you add together to TriangleLayer
.
Build and run the app to run across the current land of the blitheness; as the oval wobbles, each corner of the triangle begins to appear until all three corners are visible, like so:
Completing The Morph
To consummate the morphing process, you'll rotate HolderView
by 360 degrees while you contract OvalLayer
, leaving just TriangleLayer
lonely.
Open HolderView.swift add the following code to the end of drawAnimatedTriangle()
:
NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: "spinAndTransform", userInfo: nil, repeats: false)
This sets up a timer to fire subsequently the triangle animation has finished. The 0.9s time was one time once again determined by trial and error.
Now add the following part to the bottom of the grade:
func spinAndTransform() { // one layer.anchorPoint = CGPointMake(0.5, 0.half-dozen) // ii var rotationAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z") rotationAnimation.toValue = CGFloat(M_PI * ii.0) rotationAnimation.duration = 0.45 rotationAnimation.removedOnCompletion = true layer.addAnimation(rotationAnimation, forKey: null) // iii ovalLayer.contract() }
The timer you created only before calculation this code calls this function once the the oval stops wobbling and all corners of the triangle announced. Here'due south a wait at this role in more particular:
- Update the ballast point of the layer to exist slightly below the center of the view. This affords a rotation that appears more natural. This is because the oval and triangle are actually get-go from the center of the view, vertically. So if the view was rotated around its center, then the oval and triangle would appear to move vertically.
- Apply a
CABasicAnimation
to rotate the layer 360 degrees, ortwo*Pi
radians. The rotation is around the z-axis, which is the axis going into and out of the screen, perpendicular to the screen surface. - Call
contract()
onOvalLayer
to perform the animation that reduces the size of the oval until it's no longer visible.
Now open OvalLayer.swift and add the following code to contract()
:
func contract() { var contractAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path") contractAnimation.fromValue = ovalPathLarge.CGPath contractAnimation.toValue = ovalPathSmall.CGPath contractAnimation.duration = animationDuration contractAnimation.fillMode = kCAFillModeForwards contractAnimation.removedOnCompletion = false addAnimation(contractAnimation, forKey: nil) }
This sets OvalLayer
dorsum to its initial path of ovalPathSmall
by applying a CABasicAnimation
. This is the exact reverse of aggrandize()
, which y'all chosen at the outset of the animation.
Build and run your app; the triangle is the only affair that should be left on the screen one time the blitheness is done:
Cartoon The Container
In this next part, you're going to breathing the drawing of a rectangular container to create an enclosure. To do this, you'll use the stroke property of RectangleLayer
. You'll do this twice, using both ruby and blue as the stroke colour.
Open HolderView.swift and declare 2 RectangularLayer
constants every bit follows, underneath the triangleLayer
property you added before:
allow redRectangleLayer = RectangleLayer() let blueRectangleLayer = RectangleLayer()
Next add the following code to the terminate of spinAndTransform()
:
NSTimer.scheduledTimerWithTimeInterval(0.45, target: self, selector: "drawRedAnimatedRectangle", userInfo: nil, repeats: simulated) NSTimer.scheduledTimerWithTimeInterval(0.65, target: self, selector: "drawBlueAnimatedRectangle", userInfo: nil, repeats: simulated)
Hither you create two timers that call drawRedAnimatedRectangle()
and drawBlueAnimatedRectangle()
respectively. Yous draw the ruby-red rectangle showtime, right later the rotation animation is complete. The blue rectangle'south stroke begins as the crimson rectangle's stroke draws close to completion.
Add together the following two functions to the bottom of the class:
func drawRedAnimatedRectangle() { layer.addSublayer(redRectangleLayer) redRectangleLayer.animateStrokeWithColor(Colors.red) } func drawBlueAnimatedRectangle() { layer.addSublayer(blueRectangleLayer) blueRectangleLayer.animateStrokeWithColor(Colors.blue) }
Once y'all add together the RectangleLayer
as a sublayer to HolderView
, you call animateStrokeWithColor(color:)
and pass in the appropriate color
to animate the drawing of the border.
Now open RectangleLayer.swift and populate animateStrokeWithColor(color:)
as follows:
func animateStrokeWithColor(color: UIColor) { strokeColor = color.CGColor var strokeAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd") strokeAnimation.fromValue = 0.0 strokeAnimation.toValue = i.0 strokeAnimation.elapsing = 0.4 addAnimation(strokeAnimation, forKey: nil) }
This draws a stroke
around RectangleLayer
by adding a CABasicAnimation
to information technology. The strokeEnd
key of CAShapeLayer
indicates how far around the path to stop stroking. By animating this property from 0 to 1, you create the illusion of the path being fatigued from start to finish. Animating from ane to 0 would create the illusion of the entire path being rubbed out.
Build and run your app to run across how the two strokes expect as they build the container:
Filling In The Container
With your container now in place, the adjacent phase of the animation is to fill it up. The effect you're looking for is that of water filling up a glass. This is a great visual effect and sets things up for a big…splash! :]
Open up HolderView.swift and add the following abiding just beneath the two RectangleLayer
properties:
let arcLayer = ArcLayer()
Now add the following code to the terminate of drawBlueAnimatedRectangle()
:
NSTimer.scheduledTimerWithTimeInterval(0.40, target: self, selector: "drawArc", userInfo: nil, repeats: false)
This creates a timer to phone call drawArc()
in one case the blue RectangleLayer
finishes drawing.
Add the following function to the stop of the class:
func drawArc() { layer.addSublayer(arcLayer) arcLayer.animate() }
This adds the case of ArcLayer
created above to the HolderView
's layer before you animate in the fill.
Open ArcLayer.swift and add together the post-obit code to animate()
:
func animate() { var arcAnimationPre: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationPre.fromValue = arcPathPre.CGPath arcAnimationPre.toValue = arcPathStarting.CGPath arcAnimationPre.beginTime = 0.0 arcAnimationPre.duration = animationDuration var arcAnimationLow: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationLow.fromValue = arcPathStarting.CGPath arcAnimationLow.toValue = arcPathLow.CGPath arcAnimationLow.beginTime = arcAnimationPre.beginTime + arcAnimationPre.duration arcAnimationLow.duration = animationDuration var arcAnimationMid: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationMid.fromValue = arcPathLow.CGPath arcAnimationMid.toValue = arcPathMid.CGPath arcAnimationMid.beginTime = arcAnimationLow.beginTime + arcAnimationLow.duration arcAnimationMid.duration = animationDuration var arcAnimationHigh: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationHigh.fromValue = arcPathMid.CGPath arcAnimationHigh.toValue = arcPathHigh.CGPath arcAnimationHigh.beginTime = arcAnimationMid.beginTime + arcAnimationMid.duration arcAnimationHigh.elapsing = animationDuration var arcAnimationComplete: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationComplete.fromValue = arcPathHigh.CGPath arcAnimationComplete.toValue = arcPathComplete.CGPath arcAnimationComplete.beginTime = arcAnimationHigh.beginTime + arcAnimationHigh.duration arcAnimationComplete.duration = animationDuration var arcAnimationGroup: CAAnimationGroup = CAAnimationGroup() arcAnimationGroup.animations = [arcAnimationPre, arcAnimationLow, arcAnimationMid, arcAnimationHigh, arcAnimationComplete] arcAnimationGroup.duration = arcAnimationComplete.beginTime + arcAnimationComplete.duration arcAnimationGroup.fillMode = kCAFillModeForwards arcAnimationGroup.removedOnCompletion = imitation addAnimation(arcAnimationGroup, forKey: goose egg) }
This blitheness is very similar to the earlier wobble animation; you create a CAAnimationGroup
that contains v instances of a path-based CABasicAnimation
. Each path has a slightly different arc with increasing height and is function of the starter project. Finally, you employ the CAAnimationGroup
to the layer and instruct it to not exist removed on completion and so information technology will retain its state when the animation has finished.
Build and run your app to watch the magic unfold!
Completing The Animation
All that'south left to practise is expand the blue HolderView
to fill in the entire screen and add a UILabel
to the view to serve as the logo.
Open HolderView.swift and add together the post-obit code to the end of drawArc()
:
NSTimer.scheduledTimerWithTimeInterval(0.90, target: self, selector: "expandView", userInfo: nil, repeats: false)
This creates a timer that calls expandView()
after the ArcLayer
fills up the container.
Now, add the post-obit role to the lesser of the same class:
func expandView() { // ane backgroundColor = Colors.blue // ii frame = CGRectMake(frame.origin.x - blueRectangleLayer.lineWidth, frame.origin.y - blueRectangleLayer.lineWidth, frame.size.width + blueRectangleLayer.lineWidth * two, frame.size.elevation + blueRectangleLayer.lineWidth * 2) // 3 layer.sublayers = nil // iv UIView.animateWithDuration(0.3, filibuster: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { self.frame = self.parentFrame }, completion: { finished in cocky.addLabel() }) }
Here's what that method does:
- The background of the holder view is set to blueish, to match the color you filled the rectangle with.
- The frame is expanded to business relationship for the
RectangleLayer
's stroke width that you lot added earlier. - All sublayers are removed. Now at that place are no oval, no triangle and no rectangle layers.
- An animation is added to aggrandize the
HolderView
to fill the screen. Once that blitheness's done, you calladdLabel()
.
Add the following function to the bottom of the class:
func addLabel() { delegate?.animateLabel() }
This only calls the view's delegate role to animate the characterization.
Now open ViewController.swift and add the following lawmaking to animateLabel()
:
func animateLabel() { // 1 holderView.removeFromSuperview() view.backgroundColor = Colors.blue // 2 var characterization: UILabel = UILabel(frame: view.frame) label.textColor = Colors.white characterization.font = UIFont(proper noun: "HelveticaNeue-Thin", size: 170.0) characterization.textAlignment = NSTextAlignment.Center characterization.text = "South" label.transform = CGAffineTransformScale(label.transform, 0.25, 0.25) view.addSubview(label) // iii UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.ane, options: UIViewAnimationOptions.CurveEaseInOut, animations: ({ characterization.transform = CGAffineTransformScale(characterization.transform, 4.0, 4.0) }), completion: { finished in cocky.addButton() }) }
Taking each commented department in turn:
- Remove
HolderView
from the view and gear up the view'south background colour to blue. - Create a
UILabel
with text of 'Due south' to correspond the logo, and add it to the view. - Apply a bound animation to the characterization to scale information technology in. In one case the animation is done, call
addButton()
to add a button to your view, which, when pressed, repeats the blitheness.
Build and run the awarding, give yourself a pat on the dorsum and have a moment to enjoy what you've congenital! :]
Where to Go From Here?
You can download the final completed projection here.
This tutorial covered quite a few unlike animation techniques that, when stacked together, create a rather circuitous loading animation that really makes your app smoothen on first run.
From here, feel free to play around with different timings and shapes to see what cool animations y'all can come up up with.
If you want to accept your new found animation skills to the next level, and so I suggest you check out our volume, iOS Animations by Tutorials.
I promise that you had a ton of fun going through this tutorial, and if you have any questions or comments, delight bring together the forum give-and-take below!
Source: https://www.raywenderlich.com/1746-how-to-create-a-complex-loading-animation-in-swift
Posted by: whitfieldlaysence.blogspot.com
0 Response to "How To Create Loading Animation Ios Swift"
Post a Comment