3

I'm trying to understand and experiment with swiftUI view transitions and could use some insight into why the following does not work the way I expect:

struct ContentView: View {
@State private var showOverlay: Bool = false

var body: some View {
    ZStack {
        VStack {
            Button(action: {
                withAnimation(.easeInOut) {
                    self.showOverlay = true
                }
            }) {
                Text("Hello, World!")
            }
        }
        .zIndex(0)
        .animation(nil)
        .blur(radius: showOverlay ? 3 : 0)

        if showOverlay {
            HStack {
                Button(action: {
                    withAnimation(.easeInOut) {
                        self.showOverlay = false
                    }
                }) {
                    Text("Close")
                }
                .frame(width: 80, height: 80)
                .background(Color.red)
                .transition(.move(edge: .top))

                Button(action: {
                    withAnimation(.easeInOut) {
                        self.showOverlay = false
                    }
                }) {
                    Text("Close")
                }
                .frame(width: 80, height: 80)
                .background(Color.red)
                .transition(.move(edge: .bottom))
            }
            .zIndex(1.0)
        }
    }
}

There is a conditional HStack of two buttons. When the @State parameter showOverlay is changed it is done within a withAnimation block. The transitions on the individual buttons are not honored.

If I remove the HStack, and put the zIndex(1) on each button, then the removal transition occurs, but not the insertion transition.

I want both buttons to transition in their designated ways when their group is added to the view.

Questions: Why does wrapping in an HStack cause the inner transitions to be ignored? Why does the edge transition only work in 1 direction?

orion
  • 354
  • 1
  • 13

2 Answers2

2

In order to get two different animations, you can pull the conditional logic closer to the buttons, i.e., inside the HStack:

HStack {
  if showOverlay {
    Button(action: {
      withAnimation(.easeInOut) {
        self.showOverlay = false
      }
    }) {
      Text("Close")
    }
    .frame(width: 80, height: 80)
    .background(Color.red)
    .transition(.move(edge: .top))

    Button(action: {
      withAnimation(.easeInOut) {
        self.showOverlay = false
      }
    }) {
      Text("Close")
    }
    .frame(width: 80, height: 80)
    .background(Color.red)
    .transition(.move(edge: .bottom))
  }
}
.zIndex(1.0)

This leaves you with an empty HStack when showOverlay == false, but that shouldn't be much of a problem, at least in this example.

ingoem
  • 425
  • 4
  • 6
1

Transition works for view which is appeared/disappeared. In the provided code snapshot appeared/disappeared HStack, so transition should be applied to it.

Note: transitions must be tested on Simulator or real device, most of them do not work in Preview.

Below is modified part of code which gives worked behaviour. Tested with Xcode 11.2 / iOS 13.2

enter image description here

if showOverlay {
    HStack {
        Button(action: {
            withAnimation(.easeInOut) {
                self.showOverlay = false
            }
        }) {
            Text("Close")
        }
        .frame(width: 80, height: 80)
        .background(Color.red)

        Button(action: {
            withAnimation(.easeInOut) {
                self.showOverlay = false
            }
        }) {
            Text("Close")
        }
        .frame(width: 80, height: 80)
        .background(Color.red)
    }
    .transition(.move(edge: .top)) // << only in this place needed
    .zIndex(1.0)
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 2
    I see. So in this case I couldn't apply separate unique transitions to each button? In my example one button animates from the top edge, and the other from the bottom. (My real world use is more complex than the sample posted) – orion Feb 14 '20 at 21:46