5

I have been testing how transitions work based on SwiftUI-labs but I found that transitions should not all be implemented in the same way. Based on the previous article:

Note that since XCode 11.2, transitions no longer work with implicit animations

Thing is, that I found something strange. Some transitions work fine when using either implicit animation OR by associating an animation to it. So, what animation should be used? Well, it depends. On what? I don't know. I'm hoping anyone can help me explaining all this.

In the following test, I have created 5 views, each one associated with a different transition: .opacity,.scale, .move, .slide, and combined(.opacity and .move). Here are my findings:

NOTE: I'M USING XCODE 11.4 AND THE SIMULATOR!

Transitions using implicit animation

Only .move and .slide transitions work OK (removal and insertion).

enter image description here

Transitions using explicit animation

Removal and insertion animation works OK for all transitions.

enter image description here

Associating an animation to each transition

Only .scale and .opacity transitions work OK (removal and insertion).

enter image description here

From my point of view, not having a standard way of implementing transitions complicates things, moreover when combining transitions (.combined).

Am I missing something about the implementation? Here's my code:

struct TestAnimation: View {
    @State var show : Bool = true
    var colors : [Color] = [.orange, .yellow, .green, .blue, .pink]

    var body: some View {
        VStack(alignment: .leading) {
            Spacer()

            Color.purple
                .frame(height: 100)
                .overlay(
                    Text("Tap Me!").foregroundColor(.white))
                .onTapGesture {
                    // (#1) implicit animation
                    self.show.toggle()

                    // (#2) explicit animation
                    /*withAnimation(Animation.easeInOut(duration: 1)) {
                        self.show.toggle()
                    }*/

                    // (#3) associate an animation with a transition
                    //self.show.toggle()
            }

            HStack {
                if show {
                    Rectangle()
                        .fill(colors[0])
                        .frame(width: 70, height: 100)
                        .overlay(Text("opacity"))
                        .transition(AnyTransition.opacity) // (#1) - doesn't animate, only removes/inserts the view
                        .animation(.easeInOut(duration: 1)) // (#1) 
                        //.transition(AnyTransition.opacity) // (#2) - only animates the removal, not the insertion
                        //.transition(AnyTransition.opacity.animation(.easeInOut(duration: 1))) // (#3) - animates removal and insertion

                    Rectangle()
                        .fill(colors[1])
                        .frame(width: 70, height: 100)
                        .overlay(Text("scale"))
                        .transition(AnyTransition.scale) // (#1) - doesn't animate, only removes/inserts the view
                        .animation(.easeInOut(duration: 1)) // (#1) 
                        //.transition(AnyTransition.scale) // (#2) - only animates the removal, not the insertion
                        //.transition(AnyTransition.scale.animation(.easeInOut(duration: 1))) // (#3) - animates removal and insertion

                    Rectangle()
                        .fill(colors[2])
                        .frame(width: 70, height: 100)
                        .overlay(Text("move"))
                        .transition(AnyTransition.move(edge: .bottom)) // (#1) - animates removal and insertion
                        .animation(.easeInOut(duration: 1)) // (#1) 
                        //.transition(AnyTransition.move(edge: .bottom)) // (#2) - only animates the removal, not the insertion
                        //.transition(AnyTransition.move(edge: .bottom).animation(.easeInOut(duration: 1))) // (#3) - doesn't animate, only removes/inserts the view

                    Rectangle()
                        .fill(colors[3])
                        .frame(width: 70, height: 100)
                        .overlay(Text("slide"))
                        .transition(AnyTransition.slide) // (#1) - animates removal and insertion
                        .animation(.easeInOut(duration: 1)) // (#1) 
                        //.transition(AnyTransition.slide) // (#2) - only animates the removal, not the insertion
                        //.transition(AnyTransition.slide.animation(.easeInOut(duration: 1))) // (#3) - doesn't animate, only removes/inserts the view

                    Rectangle()
                    .fill(colors[4])
                    .frame(width: 70, height: 100)
                    .overlay(Text("op&mv"))
                    .transition(AnyTransition.opacity.combined(with: .move(edge: .bottom))) // (#1) - doesn't animate, only removes/inserts the view
                    .animation(.easeInOut(duration: 1)) // (#1) 
                    //.transition(AnyTransition.opacity.combined(with: .move(edge: .bottom))) // (#2) - only animates removal, not the insertion
                        //.transition(AnyTransition.opacity.combined(with: .move(edge: .bottom)).animation(.easeInOut(duration: 1))) // (#3) - animates removal (it's not smooth), and only animates opacity on insertion
                }
            }
            .frame(height: 100)
            .padding(7)
                .border(Color.gray)
        }
    }

}
Aнгел
  • 1,361
  • 3
  • 17
  • 32
  • 2
    I would add that Preview does not always renders transitions (or not all) properly, so retest everything at least on Simulator. – Asperi Mar 31 '20 at 03:00
  • I like the thoroughness of your tests. Preview is just generally weak. It seems so not ready for prime-time. – HalR Mar 31 '20 at 03:28
  • @Asperi, I have tested everything in the simulator. The only difference is when using explicit animations: all transitions work as expected. But still, the question remains, why there's a different behavior for transitions when using implicit OR when associating an animation to it? – Aнгел Mar 31 '20 at 04:38
  • @HalR I have generated new gifs using the simulator this time. – Aнгел Mar 31 '20 at 04:53
  • 3
    If I put the testAnimation into another view and animate it every version works fine. ` var body: some View { VStack { TestAnimation() }.animation(.easeInOut) }` Just btw. – HalR Mar 31 '20 at 05:05
  • Mate I just noticed this and thought there was something I wasn't understanding. `.opacity` animated fine with transition associated with animation but `.move` did not. I had to add an animation modifier to get `.move` to work - so weird – SparkyRobinson Dec 08 '20 at 09:18
  • @HalR yep that works, that must mean it's a bug in SwiftUI then? – SparkyRobinson Dec 08 '20 at 09:26

0 Answers0