1

I've encountered an issue that I was not able to tinker to my full satisfaction.

I have a MasterView that changes environmentObject SelectionObject to show ZStack content* from Link enum. The issue is that the removal transition is almost invisible when there is a background in MasterView (Color.gray, when I set opacity, the animation is visible a little bit but unless it gets to low number, the overall opacity of FirstView or SecondView is detrimented. It works as expected without any background in MasterView

Here is my code:

class SelectionObject: ObservableObject {
    @Published var selection: Link? = nil
}

struct MasterView: View {
    @EnvironmentObject var selection: SelectionObject
    
    var body: some View {
        ZStack {
            Color.gray
            VStack {
                ForEach(Link.allCases) { menu in
                    Button(action: {
                        selection.selection = menu
                    }, label: {
                        Label(menu.title, systemImage: menu.image).padding()
                    }
                    )
                    .tag(menu)
                }
            }
            
            ForEach(Link.allCases) { menu in
                if menu == selection.selection {
                    menu.contentView
                        .transition(AnyTransition.slide)
                        .animation(.spring())
                    
                }
                
            }
        }
    }
}

struct Menu_Previews: PreviewProvider {
    static var previews: some View {
        MasterView().environmentObject(SelectionObject())
    }
}


struct FirstView: View {
    @EnvironmentObject var selection: SelectionObject
    var body: some View {
        ZStack {
            Color.orange
            VStack {
                Text("First View content")
                Button(action: {
                    selection.selection = nil
                }, label: {
                    Text("Get back with a nice animation").padding().foregroundColor(.white)
                }
                )
            }
        }
    }
}

struct SecondView: View {
    @EnvironmentObject var selection: SelectionObject
    var body: some View {
        ZStack {
            Color.orange
            VStack {
                Text("Second View content")
                Button(action: {
                    selection.selection = nil
                }, label: {
                    Text("Get back with a nice animation")
                }
                )
            }
        }
    }
}


enum Link: Int, CaseIterable, Identifiable {
    var id: Int {
        return self.rawValue
    }
    
    case first
    case second
    
    var title: LocalizedStringKey {
        switch self {
        case .first: return "First"
        case .second: return "Second"
        }
    }
    
    var image: String {
        switch self {
        case .first: return "icloud"
        case .second: return "display"
        }
    }
    
    var contentView: AnyView {
        switch self {
        case .first: return AnyView ( FirstView() )
        case .second: return AnyView ( SecondView() )
        }
    }
}

I've tried to use a zIndex way (mentioned here: Transition animation not working in SwiftUI ) but was unable to make it work as it worked only once and did not show the content on second click.

Can you help me find a way around the issue?

  • I use this because I can't use NavigationView as my MasterView is used in overlay in a different NavigationView and there is a frame, offset, and cornerRadius issue that prevents to click on anything unless I delete either the offset or cornerRadius.
cluelessCoder
  • 948
  • 6
  • 19

1 Answers1

2

Just add a zIndex to your menu.contentView and it will be always on top. Hence, you can see the back animation.

menu.contentView
    .id(UUID()) // << add id here 
    .transition(AnyTransition.slide)
    .animation(.spring())
    .zIndex(50) //<< set higher zIndex here

Works multiple times aswell, after toggling view multiple times

Edit: Transition will fade in from leading edge and will dismiss to trailing edge. As the view stay there it will fade in back (once it is called again) from the trailing edge. With id(UUID() you create a new one which fades back from leading to trailing

davidev
  • 7,694
  • 5
  • 21
  • 56
  • It unfortunately doesn't work for me multiple times -> I've [recorded a video](https://wetransfer.com/downloads/81fff7ea08edde3f0f88f8010b3e310c20201209182052/bfeb6d3835a96d37bc0055e87124085b20201209182103/63aa70), same issue is even in simulator. The first click is OK, then it doesn't do anything. Edit: the same issue remains in my app project. – cluelessCoder Dec 09 '20 at 18:22
  • Thank you for the video. I think I could recreate it.. let me check again – davidev Dec 09 '20 at 18:31
  • That is beyond me. The code doesn't even work for me even when I put it on my iPhone... PS: I even get "Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1937b3e30)" on my iPhone when I put the zIndex in my app project (the code is basically the same I just simplified the number of views and styling here) – cluelessCoder Dec 09 '20 at 18:35
  • I have updated the code.. try to ad id(UUID()) and check for the bug again – davidev Dec 09 '20 at 18:43
  • The UUID fixed it. Thank you a lot! – cluelessCoder Dec 09 '20 at 18:45
  • No problem.. just to explain: you are using the same views. Hence the will transition from left in and leave to the right. However, they stay at right and will come from there if you call it again. With adding id(UUID()) you create a new view and it comes from left again – davidev Dec 09 '20 at 18:47
  • okay, it makes sense. But a) how did it worked without zIndex and UUID when I just deleted the background?, and b) the same issue was there when the view ended where it started – when I used ".transition(AnyTransition.asymmetric(insertion: AnyTransition.flipFromRight, removal: AnyTransition.slide))" – cluelessCoder Dec 09 '20 at 18:55