如何像SwiftUI中默认的iOS导航一样,动画化一个ZStack视图在彼此之上的适当过渡?

xvw2m8pv  于 11个月前  发布在  Swift
关注(0)|答案(1)|浏览(110)

我有3个视图是完全独立的。此外,我不希望ultraThinMaterial添加为视图堆叠在对方。
当视图2从底部边缘出现在视图1上时,它也应该从底部边缘删除。
当我尝试时,要么只有一个简单的不透明动画运行,要么后面的视图也通过底部边缘过渡。
基本上是针对ZStack的视图,比如iOS默认导航是如何工作的,或者苹果Map中的工作表是如何相互叠加的。

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)))
     }
}

字符串

des4xlb0

des4xlb01#

我建议作以下修改:

  • 将过渡应用于各个视图,而不是ZStack
  • 不需要将过渡应用为不对称,因为您将使用底边进行插入和移除。
  • 不太清楚为什么有些视图有一个GeometryReader,这是不需要的动画。
  • .frame(maxWidth: .infinity, maxHeight: .infinity)应用于各个视图,以便它们填满屏幕。
  • 如果你不想让.ultraThinMaterial在每一层上都被看到,那就把它应用到ZStack上。

就像这样:

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
    }
}

字符串
希望这能让你更接近你想要达到的效果。

相关问题