SwiftUI如何摆脱NavigationView中通过NavigationLink链接的视图的不断初始化?
Posted
技术标签:
【中文标题】SwiftUI如何摆脱NavigationView中通过NavigationLink链接的视图的不断初始化?【英文标题】:SwiftUI how to get rid of constant initialization of views linked through NavigationLink in NavigationView? 【发布时间】:2021-08-23 19:43:47 【问题描述】:在下面的示例中,NavigationView 中通过 NavigationLink 链接的视图有 2 个:根 ContentView 和从属 ListView。
struct ContentView: View
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State private var counter = 0
var body: some View
NavigationView
VStack
Text("counter: \(counter)")
.onReceive(timer) _ in
counter += 1
NavigationLink(destination: ListView())
Text("List").padding()
struct ListView: View
let items = ["first", "second", "third"]
init()
print(Date(), "init ListView")
var body: some View
List
ForEach(items, id: \.self) item in
NavigationLink(destination: ItemView(item: item))
Text(item).padding()
在 ContentView 中,counter 变量每秒都在变化,这会导致 ContentView 被重绘,ListView 被永久初始化。 ListView 被初始化,并且“init ListView”每秒显示一次,无论 ContentView 或 ListView 上当前是什么。 从数据模型的角度来看,ListView 不依赖于计数器和 ContentView 的状态。
在实际应用中,情况要复杂得多,成本也高得多。 ContentView 显示状态不断变化的 MapView,ListView 显示记录的路线列表。 ListView 初始化包含一些非常昂贵的逻辑。 不断地初始化 ListView 是需要时间的,虽然它真的只是在切换到 ListView 时才需要,而不是每次 ContentView 改变状态时才需要。
在摆脱 ListView 对 ContentView 状态变化的依赖的同时,让 NavigationView 工作的正确方法是什么?
【问题讨论】:
这能回答你的问题吗? ObervableObject being init multiple time, and not refreshing my view 这是预期行为,您可以通过使用自定义 LazyView 或提供的 Lazy Views 之一来缓解它 【参考方案1】:正如 lorem ipsum 所说,内容的初始化和更新是 SwiftUI 工作方式的一部分,也是意料之中的。为了克服这个问题,只需创建一个处理计时器的新视图,这样只有你的最后一个会被更新。您的 ContentView 应该看起来更像:
struct ContentView: View
var body: some View
NavigationView
VStack
CounterView()
NavigationLink(destination: ListView())
Text("List").padding()
还有你的 CounterView :
struct CounterView: View
@State private var counter = 0
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View
Text("counter: \(counter)")
.onReceive(timer) _ in
counter += 1
但是,如果您需要访问您的 Timer 变量,而不必更新每个单独的视图,您可以考虑通过简单地调用“Timer.secondsUpdater”来使单调可访问。
extension Timer
static let secondsUpdater = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
您的 CounterView 最终会像这样调用计时器:
struct CounterView: View
@State private var counter = 0
var body: some View
Text("counter: \(counter)")
.onReceive(Timer.secondsUpdater) _ in
counter += 1
希望对你有所帮助?
【讨论】:
非常感谢我自己几乎做出了这个决定,但你的回答非常清楚地描述了一切))以上是关于SwiftUI如何摆脱NavigationView中通过NavigationLink链接的视图的不断初始化?的主要内容,如果未能解决你的问题,请参考以下文章
如何在启动时显示 NavigationView (SwiftUI) 的详细视图
SwiftUI:如何更新 NavigationView 详细信息屏幕?
如何在 SwiftUI 中隐藏 NavigationView Bar
如何使用 SwiftUI 消除嵌套 NavigationView 中的空间