Chapters

Hide chapters

iOS Animations by Tutorials

Sixth Edition · iOS 13 · Swift 5.1 · Xcode 11

Section IV: Layer Animations

Section 4: 9 chapters
Show chapters Hide chapters

2. Intermediate SwiftUI Animations
Written by Marin Todorov

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

You hopefully completed the previous chapter successfully and worked through the exercises too — you’re on the path to becoming a great SwiftUI animator!

As you remember, SwiftUI works by you declaring how your UI should look for a given set of data. If the data changes, your UI is rebuilt. You can add modifiers to part or all of the UI to let the framework know that, if those parts change, you’d like the change to be animated. Your job is mostly to describe what kind of animation (basic or spring) you would like for SwiftUI to automatically apply between the “snapshots” of your view hierarchy when you make changes to your state data.

There is not much more than this basic principle to learn in order to create stunning animations with SwiftUI! That’s a rather stark difference when comparing with animations via UIKit or AppKit.

In this chapter, you will iterate over drawing shapes and animating them on screen. You will also learn about view transitions, and will create a more complex, interactive multi-stage animation.

Getting started

The final animation you will build by working through the end of this chapter will be a modern, fluid spinner-view that will look like this:

The beginnings of that spinner, however, are rather humble.

To get started, open the starter project for this chapter and click SpinnerView.swift. Then, check out the starter view layout in the Editor Canvas:

This is where you start on your path to the spinner view you saw earlier. Currently on screen, you see a single ellipse shape that we’ll call a “leaf” throughout this chapter.

Have a look at the starter code of SpinnerView and observe that:

  • Leaf is a view type nested inside SpinnerView — since views are just structs, you can treat them as any other type in your code base. You can nest views, make them public or private, make them conform to further protocols besides View, etc.

  • Leaf draws itself by using a view type called Capsule. Capsule along with Circle, RoundedRectangle and Rectangle allow you to easily draw shapes, or use them as clipping masks.

  • The SpinnerView body is currently very simple, it includes a single leaf shape and, once added on screen, calls its animate(_) method which is currently empty.

The code is all set for you to jump in and add some coolness.

Drawing the spinner

Your first task is to draw the static spinner on screen. This will give you some insight into how to compose shape views and hopefully give you ideas how to design your own shape animations in future.

ForEach(0..<leavesCount) { index in
  Leaf()
}

let rotation: Angle
Leaf(rotation: Angle(degrees:
  (Double(index) / Double(self.leavesCount)) * 360.0)
)
.rotationEffect(rotation)

.offset(x: 0, y: 70)

Creating the basic spinning animation

To get an animation going, you will use a timer so you can repeatedly make changes to your state over time. Each time the timer fires you will animate a different leaf, creating a wave-like effect that goes round and round.

@State var currentIndex: Int?
Timer.scheduledTimer(withTimeInterval: 0.15, repeats: true, block: { timer in

})
if let current = self.currentIndex {
  self.currentIndex = (current + 1) % self.leavesCount
} else {
  self.currentIndex = 0
}
let isCurrent: Bool
Leaf(rotation: Angle(degrees:
  (Double(index) / Double(self.leavesCount)) * 360.0),
  isCurrent: index == self.currentIndex
)
.stroke(isCurrent ? Color.white : Color.gray, lineWidth: 8)

.animation(.easeInOut(duration: 0.5))

.animation(.easeInOut(duration: 1.5))
.offset(x: isCurrent ? 10 : 0, y: isCurrent ? 40 : 70)

.scaleEffect(isCurrent ? 0.5 : 1.0)

Adding multiple animation stages

I bet you still remember that all layout changes on screen in SwiftUI are triggered by changes to your state. Currently, you have a single state property which sets which leaf is currently animated.

@State var completed = false
var iteration = 0
iteration += 1
if iteration == 60 {
  timer.invalidate()
  self.completed = true
}

let isCompleting: Bool
Leaf(
  rotation: Angle(degrees:
  (Double(index) / Double(self.leavesCount)) * 360.0),
  isCurrent: index == self.currentIndex,
  isCompleting: self.completed
)
.rotationEffect(isCompleting ? .zero : rotation)

.frame(width: 20, height: isCompleting ? 20 : 50)
self.currentIndex = nil

Adding view transitions

You’ve seen how to create SwiftUI animations between different states of views in your view hierarchy. But you can also create animation effects called transitions which deal with the situations when you add or remove a view to/from your view hierarchy.

@State var isVisible = true
if isVisible {
  ZStack {
    ForEach(0..leavesCount) { index in
	  ... 
	}
  }.onAppear(perform: animate)
}
func complete() {
  guard !completed else { return }

  completed = true
  currentIndex = nil
  delay(seconds: 2) {
   self.isVisible = false
  }
}
self.complete()
.transition(.move(edge: .top))
let shootUp = AnyTransition
  .offset(CGSize(width: 0, height: -1000))
.animation(.easeIn(duration: 1.0))
.transition(shootUp)

Interactive animations

At this point, you know plenty about creating animations with SwiftUI but there is one last topic you will cover in this chapter: Creating beautiful animations driven by the user.

@State var currentOffset = CGSize.zero
.gesture(
  DragGesture()
    .onChanged { gesture in
      self.currentOffset = gesture.translation
    }
    .onEnded { gesture in
      if self.currentOffset.height > 150 {
        self.complete()
      }
      self.currentOffset = .zero
    }
)
.offset(currentOffset)
.blur(radius: currentOffset == .zero ? 0 : 10)
.animation(.easeInOut(duration: 1.0))

Key points

  • Shapes in SwiftUI adhere to the View protocol so you can build intriguing animations including transforming, fading in and out and morphing shapes just like you do for any other animation in your UI.
  • You can add more state to your view types to be able to drive more complex, potentially multi-stage animations.
  • Incorporating gesture input in your view state is a matter of adding few more modifiers to your views so building user interactive animations is a piece of cake.

Where to go from here?

So far in this book, you learned how to create basic and spring animations with SwiftUI. You’ve built quite complex visual effects that span over different stages, and that are also driven by the user. You’re on a good path to building engaging user interfaces with SwiftUI.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now