当视图层次结构不改变时,具有可编程导航的 SwiftUI 动画

问题描述 投票:0回答:1

我正在编写一个类似 anki 的应用程序,带有语言练习。完成练习后,您将进入下一个单词的练习,依此类推,无限期地进行。我想使用导航在单词之间进行转换。这是我想出的一个简化示例:

struct ContentView: View {
    @State private var page: Int = 0
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                NavigationLink("Go to page \(page)", value: page)
            }
            .navigationDestination(for: Int.self) { currentPage in
                VStack {
                    Text("You selected \(currentPage)")
                    Button("Next page") {
                        page += 1
                        path.removeLast()
                        path.append(page)
                    }
                }.onAppear() {
                    print("View with page \(page) is appearing")
                }
            }
        }
    }
}

我使用带有推送和弹出功能的导航路径,将最上面的视图“替换”为下一个单词练习。请注意,我不想只是将视图“推入”堆栈,因为这样它将无限期地增长。我还希望通过“后退”按钮进入主菜单,而不是之前的练习。提供的代码有效,但转换到下一页时没有动画。此外,导航到下一页时不会调用“onAppear”方法。

怀疑这与视图层次结构没有改变有关,我尝试使用 .id 修饰符使 VStack 视图的 id 依赖于页面,并将不同类型的对象推送到路径中 - 这些都没有帮助。

如果有任何关于如何解决这个问题的想法,我将非常感激!

swiftui
1个回答
0
投票

按照您的方式,没有发生导航。您不会创建新视图,您只是更新同一视图 (ContentView) 中的值。从逻辑上讲,因为没有导航,所以没有导航过渡动画。这也是

.onAppear
不触发的原因,因为根视图永远不会消失。

要实际导航,请为新页面创建一个新结构,该结构接受必要的值,例如页面和路径。然后,在

.navigationDestination
中调用它。

由于您的导航模型很简单,基于数字,因此您可以通过使用 Int 数组作为路径而不是 NavigationPath 类型来让您的生活更轻松,我相信这允许类型擦除 - 这意味着它可以容纳任何东西,而不仅仅是Int 类型。下面的代码以 Int 数组为例。

如果只需要返回根,则不需要压入和弹出来管理路径堆栈。您可以保持简单并继续推动,直到弹出所有内容以转到根视图。

尝试一下:

import SwiftUI

struct PopNavigation: View {
    
    //State values
    @State private var page: Int = 1
    @State private var path: [Int] = []
    
    //Body
    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                NavigationLink("Go to page \(page)", value: page)
            }
            .navigationDestination(for: Int.self) { currentPage in
                NavPageView(page: $page, path: $path)
                    
            }
            .navigationTitle("Main")
        }
    }
}

struct NavPageView: View {
    
    //Parameters
    @Binding var page: Int
    @Binding var path: [Int]
    
    //Body
    var body: some View {
        VStack {
            Text("You selected \(page)")
            
            Button {
                page += 1
                path.append(page)
            } label: {
                Text("Next page")
            }
        }
        .onAppear() {
            print("View with page \(page) is appearing")
        }
        .navigationTitle("Page \(page)")
        .navigationBarTitleDisplayMode(.inline)
        .navigationBarBackButtonHidden() //hide default back button
        .toolbar {
            ToolbarItem(placement: .topBarLeading) {
                
                //Custom back button
                Button {
                    //Back to root
                    path.removeLast(path.count)
                } label: {
                    HStack {
                        Image(systemName: "chevron.left")
                        Text("Back to Main")
                    }
                }
            }
        }
    }
}

#Preview {
    PopNavigation()
}
© www.soinside.com 2019 - 2024. All rights reserved.