3

With the following code, the intention is to have the transition fading in (opacity) and shifting downwards (offset) at the same time but with a delay of .3 seconds.

With this one only the opacity transition is visible after 0.3 seconds:

.transition(.offset(x: 0, y: -20)
.combined(with: .opacity)
.animation(.easeOut.delay(0.3)))

I thought maybe the animation has to be set on both transitions, the initial AND the combined one like so (spoiler: same results as before):

.transition(.offset(x: 0, y: -20)
.combined(with:.opacity.animation(.easeOut.delay(0.5)))
.animation(.easeOut.delay(0.5)))

Changing the order doesn't change the outcome either:

.transition(.offset(x: 0, y: -20)
.animation(.easeOut.delay(0.5))
.combined(with: .opacity.animation(.easeOut.delay(0.5))))

So what am I doing wrong here? How can I have a combined transition delayed?

mahan
  • 12,366
  • 5
  • 48
  • 83
Leo
  • 1,508
  • 13
  • 27

1 Answers1

2

Try this

extension AnyTransition {
    static var delayAndFade: AnyTransition {
        return AnyTransition.identity
              .combined(with: .opacity)
              .animation(.default.delay(3))
    }
}

If you want to move a view, you should animate its offset using the withAnimation function.

         Text("Move and fade.")
             .offset(y: offset)
             .transition(.delayAndFade)

struct ContentView: View {
    @State private var showDetails = false
    @State var offset:CGFloat = 0

    var body: some View {
        VStack {
            Button("Press to show details") {
                showDetails.toggle()
                withAnimation(.default.delay(3)) {
                    self.offset = -20
                }
            }

        
            if showDetails {
                Text("Move and fade.")
                    .offset(y: offset)
                    .transition(.delayAndFade)
            }
        }
    }
}

Update

extension AnyTransition {
    static var moveAndFade: AnyTransition {
        return AnyTransition.move(edge: .top)
              .combined(with: .opacity)
    }
}

Try this

HStack {
     Text("Move and fade.")
}
.animation(Animation.default.delay(2))
.transition(.moveAndFade)

It works with all kind of views except Text.

struct ContentView: View {
    @State private var showDetails = false
    @State var offset:CGFloat = 0

    var body: some View {
        VStack {
            Button("Press to show details") {
                showDetails.toggle()

            }

        
            if showDetails {
                
                // Works!
                HStack {
                    Text("Move and fade.")
                }
                .animation(Animation.default.delay(2))
                .transition(.moveAndFade)
                
                Button("Move and fade.") {}
                .animation(Animation.default.delay(2))
                .transition(.moveAndFade)
                
                // Does not work
                Text("Move and fade.")
                .animation(Animation.default.delay(2))
                .transition(.moveAndFade)
            }
        }
    }
}

mahan
  • 12,366
  • 5
  • 48
  • 83
  • thx for your answer, I guess you ment -20 for th eoffset? But anyways, it still entirely skips the offset transmission. I btw noticed the same behaviour with .move. Is this a bug or a limitation? – Leo Sep 30 '21 at 15:45
  • @lenny check my answer. Just updated. – mahan Sep 30 '21 at 16:12
  • thx, that does work, but we now shifted the scope a bit. My initial question was not specific to offset and opacity but to combined transitions in general. Because even when I use .move(edge: .leading) and .opacity together as a combined transition, it doesn't work with delays using any of the approaches mentioned here so far. – Leo Sep 30 '21 at 16:47
  • @lenny check the update – mahan Oct 01 '21 at 09:10