在 SwiftUI 中使用 onAppear 时出现无限循环
Posted
技术标签:
【中文标题】在 SwiftUI 中使用 onAppear 时出现无限循环【英文标题】:Infinite loop when using onAppear in SwiftUI 【发布时间】:2021-10-04 20:13:24 【问题描述】:我在使用 onAppear 时遇到了一个奇怪的无限循环,我无法确定问题的根源。只有当视图是导航视图的详细视图时才会发生这种情况,但当它是根视图时它可以正常工作。 另一个有趣的事情是,如果我将详细视图包装在 NavigationView 中(所以,现在我们在导航视图中有一个导航视图),那么问题就不会再出现了。这是 SwiftUI 中的错误吗?从概念上讲,我的设计可以吗?我的意思是,使用类似 viewDidLoad 的 onAppear 来触发初始序列。 感谢您的建议。
这里是源代码。 ContentView.swift:
import SwiftUI
struct ContentView: View
@StateObject var viewModel = ContentViewModel()
var body: some View
NavigationView
VStack
Group
switch viewModel.state
case .loading:
Text("Loading...")
case .loaded:
HStack
Text("Loaded")
Button("Retry")
viewModel.fetchData()
.padding(.bottom, 20)
NavigationLink("Go to detail screen", destination: DetailView())
.onAppear()
viewModel.fetchData()
class ContentViewModel: ObservableObject
enum State
case loading
case loaded
@Published var state: State = .loading
func fetchData()
state = .loading
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
self.state = .loaded
这里是详细视图的代码:
import SwiftUI
struct DetailView: View
@StateObject var viewModel = DetailViewModel()
var body: some View
Group
switch viewModel.state
case .loading:
Text("Loading...")
case .loaded:
HStack
Text("Loaded")
Button("Retry")
viewModel.fetchData()
.onAppear()
print("infinite loop here")
viewModel.fetchData()
class DetailViewModel: ObservableObject
enum State
case loading
case loaded
@Published var state: State = .loading
func fetchData()
state = .loading
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
self.state = .loaded
我在这里附上项目: https://www.dropbox.com/s/5alokj3q81jbpj7/TestBug.zip?dl=0
我使用的是 Xcode 版本 12.5.1 (12E507) 和 ios 14.5
非常感谢。
【问题讨论】:
似乎在 iOS 15 上按预期工作 这里一样,用 Xcode 13 beta 5 测试没有任何问题 为合唱添加另一个声音。 13RC、iOS 14.4 和 iOS 15。两者都经过测试。您确定这段代码导致了无限循环吗? 每个人都在评论它是如何在错误的 Xcode 版本上工作的。刚刚测试过,问题如 Xcode 12.5.1 和 iOS 14.5 所述。 好的,我刚刚意识到问题出在 Group 和 xcode 12 上。如果我使用 VStack,它可以工作。所以,这是一个苹果虫。 【参考方案1】:此问题是带有 Group 的 iOS 14 中的一个错误,即使在使用 Xcode 13 构建时仍会发生。
发生的事情是 SwiftUI 运行时弄乱了它的视图差异,所以它认为它“出现”时实际上只是重新加载,因此没有消失(这是调用 onAppear
的要求,因此错误)。
为了弥补这个问题,您需要使用初始化程序而不是依赖onAppear
。
我已经修改了你的代码,这个修改后的版本不会无限循环。
struct DetailView: View
@StateObject var viewModel = DetailViewModel()
var body: some View
Group
switch viewModel.state
case .loading:
Text("Loading...")
case .loaded:
HStack
Text("Loaded")
Button("Retry")
viewModel.fetchData()
class DetailViewModel: ObservableObject
enum State
case loading
case loaded
@Published var state: State = .loading
init()
self.fetchData()
func fetchData()
state = .loading
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
self.state = .loaded
希望项目顺利!
【讨论】:
也许该错误与 Group 有关。我刚刚意识到,如果我使用 VStack 而不是 Group,它可以工作。 我同意您的测试答案,但错误在于 Group(和 Xcode 12)。谢谢! 哦,我完全错过了你在使用一个组!很好,我会更新我的答案:) 感谢您的回答!这也发生在 iOS 15 中,并且在我发现这一点之前已经挣扎了很长时间。【参考方案2】:我在 iOS 15 中看到了类似的情况,并将所有 onAppear 内容移至初始化程序修复的内容。 没有更多的无限循环试图使对话框出现。奇怪的是,这只发生在特定状态的设置中。
在 onAppear 中,我的代码如下:
useSecondary = data.settings.showSecondaryAxis
...
但在 init 函数中需要这个来完成这项工作:
init(data: Binding<PlotData>
self._data = data
_useSecondary = State(initialValue: data.wrappedValue.settings.showSecondaryAxis)
...
… 很麻烦,但很管用。
【讨论】:
以上是关于在 SwiftUI 中使用 onAppear 时出现无限循环的主要内容,如果未能解决你的问题,请参考以下文章
在 SwiftUI 视图中结合 onChange 和 onAppear 事件?
SwiftUI解决List子项无法正确触发onAppear和onDisappear事件的问题
SwiftUI解决List子项无法正确触发onAppear和onDisappear事件的问题