NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

How to override animations with transactions

Paul Hudson    @twostraws   

Updated for Xcode 12.5

SwiftUI provides a withTransaction() function that allows us to override animations at runtime, for example to remove an implicit animation and replace it with something custom.

For example, this code toggles some text between small and large sizes, animating all the way because it has an implicit animation attached:

struct ContentView: View {
    @State private var isZoomed = false

    var body: some View {
        VStack {
            Button("Toggle Zoom") {
                isZoomed.toggle()
            }

            Spacer()
                .frame(height: 100)

            Text("Zoom Text")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)
                .animation(.easeInOut(duration: 2))
        }
    }
}

Transactions allow us to override existing animations on a case by case basis. For example, you might decide that in one particular circumstance you want the text’s animation to happen in a fast, linear way rather than it’s existing animation.

To do that, first create a new Transaction instance using whatever animation you want, then set its disablesAnimations value to true so you override any existing animations that would apply. When you’re ready, call withTransaction() using your transaction object, then go ahead and adjust all the state you want to change – it will all be animated using your transaction.

To demonstrate this in action, here’s our same text scaling example code except using a transaction to insert a custom animation that overrides the implicit one:

struct ContentView: View {
    @State private var isZoomed = false

    var body: some View {
        VStack {
            Button("Toggle Zoom") {
                var transaction = Transaction(animation: .linear)
                transaction.disablesAnimations = true

                withTransaction(transaction) {
                    isZoomed.toggle()
                }
            }

            Spacer()
                .frame(height: 100)

            Text("Zoom Text")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)
                .animation(.easeInOut(duration: 2))
        }
    }
}

For even more control you can attach a transaction() modifier to any view you want, allowing you to override any transactions that apply to the view.

For example, we could add a second zooming text view to our example, still using a transaction to trigger the zoom animation, but this time we’re going to use the transaction() modifier on the second text view so we disable any transactions on that one view – we’re overriding the override, in effect:

struct ContentView: View {
    @State private var isZoomed = false

    var body: some View {
        VStack {
            Button("Toggle Zoom") {
                var transaction = Transaction(animation: .linear)
                transaction.disablesAnimations = true

                withTransaction(transaction) {
                    isZoomed.toggle()
                }
            }

            Spacer()
                .frame(height: 100)

            Text("Zoom Text 1")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)

            Spacer()
                .frame(height: 100)

            Text("Zoom Text 2")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)
                .transaction { t in
                    t.animation = .none
                }
        }
    }
}
Hacking with Swift is sponsored by Emerge

SPONSORED Emerge helps iOS devs write better, smaller apps by profiling binary size on each pull request and surfacing insights and suggestions. Companies using Emerge have reduced the size of their apps by up to 50% in just the first day. Built by a team with years of experience reducing app size at Airbnb.

Set up a demo!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Similar solutions…

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.