SwiftUI|让 NavigationLink 惰性加载
使用惰性视图让 SwiftUI 的 NavigationLink 惰性加载导航目标视图。
最近我在使用 NavigationLink 的时候发现,一旦视图被加载,NavigationLink 里面的 View 也会被创建和初始化。
例如下面代码创建了一个带有 NavigationLink 的视图。
struct MainPanelView: View {
var body: some View {
VStack {
HStack {
NavigationLink("Titile") {
let model = ...
return ItemView(model: model)
}
Spacer()
}
// ...
}
}
}
我预想的是,当用户点击 NavigationLink 时,内部的 ItemView 才会被创建和加载。这样创建 ItemView 所用的 model 就能根据点击时的页面状态生成。
但事与愿违,一旦 MainPanelView 被显示,NavigationLink 内部的 ItemView 也会被创建。ItemView 似乎被当作了 MainPanelView 的一个子视图——当 MainPanelView 显示时,系统认为 ItemView 也要显示,所以就把它也一起创建了。这样就导致我给 ItemView 的初始化方法传递的参数都是一些默认值,并不是用户点击时的页面数据。
事实上,NavigationLink 会在其被创建时,立即创建它的目标视图(destination view)。这一行为甚至会在 NavigationLink 没有展示在屏幕上时触发。这一点和 UIKit/AppKit 框架不同。
解决这个问题需要使用一个懒加载的视图将目标视图包裹一下。
public struct LazyView<Content: View>: View {
let build: () -> Content
public init(_ build: @escaping () -> Content) {
self.build = build
}
public var body: Content {
build()
}
}
这样就会在 SwiftUI 想要渲染 LazyView 的 body 时再创建导航目标视图,做到按需创建。