0

I have 3 views that are completely separate. I furthermore, do not want the ultraThinMaterial to add on as views stack over each other.

When View 2 appears over view 1 from the bottom edge, it should get removed from the bottom edge as well.

When I tried, either only a simple opacity animation runs or the view behind gets transitioned through the bottom edge as well..

Basically aiming for ZStack of views like how the iOS default navigation works or how the sheet in Apple Maps get overlaid on top of each other.

enum ViewToShow: Identifiable {
    var id: ViewToShow {
        return self
    }
    
    case view1
    case view2
    case view3
}

@Observable
class SheetStackModel {
    var viewsItemManagedByThisSheet: [ViewToShow] = []
    
    init() {
        pushIntoStack(view: .view1)
    }
    
    func getLastView() -> ViewToShow? {
        return viewsItemManagedByThisSheet.last
    }
    
    func pushIntoStack(view: ViewToShow) {
        withAnimation(.default.speed(1.8)) {
            viewsItemManagedByThisSheet.append(view)
        }
    }
    
    func popOutOfStack() {
        withAnimation(.default.speed(1.8)) {
            _ = viewsItemManagedByThisSheet.removeLast()
        }
    }
}

struct View1: View {
    @Environment(SheetStackModel.self) var sheetStackModel
    
    var body: some View {
        GeometryReader { localGeo in
            VStack {
                Text("View 1")
                Button(action: {
                    sheetStackModel.pushIntoStack(view: .view2)

                }, label: {
                    Text("Go to View 2")
                })
            }
        }
    }
}

struct View2: View {
    @Environment(SheetStackModel.self) var sheetStackModel
         
    var body: some View {
        GeometryReader { localGeo in
            VStack {
                Text("View 2")
                Button(action: {
                    sheetStackModel.popOutOfStack()
                }, label: {
                    Text("Go back to View 1")
                })
                
                Button("Go to Detail 3") {
                    sheetStackModel.pushIntoStack(view: .view3)
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(.ultraThinMaterial)
    }
}

struct DetailView3: View {
    @Environment(SheetStackModel.self) var sheetStackModel
    
    var body: some View {
        VStack {
            Text("Even more detail View")
            
            Button("go back to view 2") {
                sheetStackModel.popOutOfStack()
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(.ultraThinMaterial)
    }
}

struct ContentView: View {
     var body: some View {
           ZStack {
                            if let lastView = sheetStackModel.getLastView()  {
                                switch lastView {
                                case .view1:
                                    View1()
                                case .view2:
                                    View2()
                                case .view3:
                                    DetailView3()
                                }
                            }
                        }
                        .transition(.asymmetric(insertion: .move(edge: .bottom), removal: .move(edge: .bottom)))
     }
}

1 Answer 1

0

I would suggest the following changes:

  • Apply the transition to the individual views, instead of to the ZStack.
  • The transitions do not need to be applied as asymmetric, because you are using the bottom edge for both insertion and removal.
  • Not quite sure why some views had a GeometryReader, this is not needed for the animation.
  • Apply .frame(maxWidth: .infinity, maxHeight: .infinity) to the individual views, so that they fill the screen.
  • If you don't want the .ultraThinMaterial to be seen on each layer then apply it to the ZStack instead.

Like this:

struct View1: View {
    @Environment(SheetStackModel.self) var sheetStackModel

    var body: some View {
        // No GeometryReader needed
        VStack {
            // content as before
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity) // <- ADDED
    }
}

struct View2: View {
    @Environment(SheetStackModel.self) var sheetStackModel

    var body: some View {
        // No GeometryReader needed
        VStack {
            // content as before
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        // .ultraThinMaterial background removed
    }
}

struct DetailView3: View {
    @Environment(SheetStackModel.self) var sheetStackModel

    var body: some View {
        VStack {
            // content as before
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        // .ultraThinMaterial background removed
    }
}

struct ContentView: View {
    @Environment(SheetStackModel.self) var sheetStackModel // <- ADDED

    var body: some View {
        ZStack {
            if let lastView = sheetStackModel.getLastView()  {
                switch lastView {

                // transitions added to separate views
                case .view1:
                    View1()
                        .transition(.move(edge: .bottom))
                case .view2:
                    View2()
                        .transition(.move(edge: .bottom))
                case .view3:
                    DetailView3()
                        .transition(.move(edge: .bottom))
                }
            }
        }
        .background(.ultraThinMaterial) // <- Moved to here
    }
}

Hope this gets you closer to the effect you are trying to achieve.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.