SwiftUI 中,NavigationStack 和 NavigationView 都可用于构建导航界面,首要区别是:

  1. avigationStack 是新一代导航容器,NavigationView 将被逐渐筛选。
  2. NavigationStack 运用途径来界说导航状况,更灵活
  3. 更加节省资源

NavigationView比如

NavigationStack inSwiftUI

struct NavigationStackSample: View {
    private var countries: [String] = ["China", "Japan", "France", "Korea", "United state"]
    var body: some View {
        NavigationView {
            ScrollView {
                VStack(spacing: 40, content: {
                    ForEach(countries, id: .self) { country in
                        NavigationLink {
                            CountryView(country: country)
                        } label: {
                            Text(country)
                        }
                    }
                })
            }
            .navigationTitle("NavigationStack")
        }
    }
}
struct CountryView: View {
    var country: String
    init(country: String) {
        self.country = country
        print("【Log】: (country)")
    }
    var body: some View {
        Text(country)
    }
}

比如中运用数组循环了一个列表,点击列表中的列,能够导航到下一级页面

咱们能够看到控制台输出的log,能够确定循环的列表中的页面都现已被初始化

NavigationStack inSwiftUI

如果数组有100条数据,那么就会初始化CountryView 100 次。不论用不用,都先初始化了,这样其实很浪费资源,100个不一定你每一个都会去看。如果是新闻列表,如果你不持续把列表往下翻,那么屏幕以外加载的数据就白白浪费了

NavigationStack中类型绑定

咱们把上述比如中运用的NavigationView改成NavigationStack看看作用。

NavigationStack需要和一些固定的调配来运用,下面是官方的一个示例

NavigationStack(path: $presentedParks) {
         List(parks) { park in
             NavigationLink(park.name, value: park)
         }
         .navigationDestination(for: Park.self) { park in
             ParkDetails(park: park)
         }
}

他需要和navigationDestination 办法来调配运用。

咱们也来模仿改改

struct NagationStackSample: View {
    private var countries: [String] = ["China", "Japan", "France", "Korea", "United state"]
    var body: some View {
        NavigationStack {
            ScrollView {
                VStack(spacing: 40, content: {
                    ForEach(countries, id: .self) { country in
                        NavigationLink(value: country) {
                            Text("country (country)")
                        }
                    }
                })
            }
            .navigationTitle("NavigationStack")
            .navigationDestination(for: String.self) { country in
                CountryView(country: country)
            }
        }
    }
}

改成以上代码后,作用和之前的NavigationView作用相同。可是控制台无任何输出。当你点击某一条数据时才会真实的去初始化

点击后代码会走navigationDestination办法,这时会真的去初始化。到目前为止一切都是正常的。咱们再增加一些数据代码,用Int类型循环一些数据

ForEach(0..<10) { i in
  NavigationLink(value: i) {
      Text("Index: (i)")
  }
}

咱们把以上代码增加到主视图的VStack里面。

NavigationStack inSwiftUI

当咱们点击新参加的行,此时却没有任何反应。控制台输了一些信息

A NavigationLink is presenting a value of type “Int” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.
Note: Links search for destinations in any surrounding NavigationStack, then within the same column of a NavigationSplitView.

意思是我在navigationDestination办法中给的是String类型,可是点击的数据是Int类型。所以导致无法工作。

此时咱们只需要再弥补一个 navigationDestination 办法即可。只不过这儿的类型要改成Int

.navigationDestination(for: Int.self, destination: { i in
    Text("View (i)")
})

这样就能够正常来运用。

所以在NavigationStack中的navigationDestination办法参数是一个要注意的当地。

struct NavigationStackSample: View {
    private var countries: [String] = ["China", "Japan", "France", "Korea", "United state"]
    var body: some View {
        NavigationStack {
 ScrollView { 
                VStack(spacing: 40, content: {
                    ForEach(countries, id: .self) { country in
                        NavigationLink(value: country) {
                            Text("country (country)")
                        }
                    }
                    ForEach(0..<10) { i in
                        NavigationLink(value: i) {
                            Text("Index: (i)")
                        }
                    }
                })
            }
           .navigationDestination(for: String.self, destination: { country in
                CountryView(country: country)
            })
            .navigationDestination(for: Int.self, destination: { i in
                Text("View (i)")
            }) 
            .navigationTitle("NavigationStack")
        }
    }
}

NavigationStack 运用途径来界说导航

自界说企图,咱们就需要运用NavigationStack初始化的另一个办法来实现。办法界说如下,它需要传入一个Binding 类型的数据.

@MainActor public init(
path: Binding<NavigationPath>, 
@ViewBuilder root: () -> Root
) where Data == NavigationPath

咱们先定一个NavigationPath()

@State private var path = NavigationPath()

将之前的办法替换为如下办法

NavigationStack(path: $path) {}

如果咱们不限制path的类型,就是用NavigationPath() 来进行初始化。如果想限制指定的类型能够运用如下代码:

@State private var path: [String] = []

上面类型限制为String,所以在列表 中,只要数据类型是String的才能够点击触发正常,其他类型就会报错。原理和上面的问题是相同的。

那么NavigationPath是什么呢?

NavigationPath 用于界说 NavigationStack 的导航状况。其首要功能包括:

  1. 描绘导航仓库,NavigationPath保护一个视图的途径仓库,表明当前的导航状况。
  2. 指定导航顺序,经过在Path内列出视图,能够界说精确的导航顺序。
  3. 标识视图,经过给视图加上标识符,能够仅有定位一个视图在途径中的位置。
  4. 当前视图,NavigationPath能够获取当前展现的视图。
  5. 呼应导航事件,途径变化时会触发回调,能够处理导航逻辑。
  6. 绑定处理,经过@Binding能够将NavigationPath绑定到视图中。

说了这么多,如同我也不明白。那么咱们来看看比如

咱们往ScrollerView里面增加以下代码:

Button {
  path.append("UK")
  path.append("India")
  path.append("Philippines")
  path.append(1000) 
} label: {
  Text("PUSH")
    .padding()
}

作用如下:

NavigationStack inSwiftUI

它会把所有放在path里面的数据进行push操作。因为我的path数据类型是NavigationPath, 所以我能够往里面增加StringInt类型的值

这样对于想跳转到指定页面的需求就很好解决了,咱们只需保护一个NavigationPath就能够完成这个需求。

悉数代码如下:

struct NavigationStackSample: View {
    private var countries: [String] = ["China", "Japan", "France", "Korea", "United state"]
 @State private var path = NavigationPath() 
    var body: some View {
 NavigationStack(path: $path) { 
            ScrollView {
                Button {
                    path.append("UK")
                    path.append("India")
                    path.append("Philippines")
                    path.append(1000) 
                } label: {
                    Text("PUSH")
                        .padding()
                }
                VStack(spacing: 40, content: {
                    ForEach(countries, id: .self) { country in
                        NavigationLink(value: country) {
                            Text("country (country)")
                        }
                    }
                    ForEach(0..<10) { i in
                        NavigationLink(value: i) {
                            Text("Index: (i)")
                        }
                    }
                })
            }
            .navigationDestination(for: String.self, destination: { country in
                CountryView(country: country)
            })
            .navigationDestination(for: Int.self, destination: { i in
                Text("View (i)")
            }) 
            .navigationTitle("NavigationStack")
        }
    }
}
struct CountryView: View {
    var country: String
    init(country: String) {
        self.country = country
        print("【Log】: (country)")
    }
    var body: some View {
        Text(country)
    }
}

大家有什么看法呢?欢迎留言讨论。
公众号:RobotPBQ