在SwiftUI中使用onAppear时的无限循环



当使用onAppear时,我有一个奇怪的无限循环,我无法识别问题的根源。只有当视图是导航视图的细节视图时才会发生这种情况,但当它是根视图时效果很好。另一件有趣的事情是,如果我在NavigationView中包装detail视图(现在我们在导航视图中有一个导航视图),那么问题就不会再出现了。这是SwiftUI的bug吗?我的设计在概念上没问题吗?我的意思是,使用onAppear像viewDidLoad触发初始序列。谢谢你的建议。

这是源代码。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
}
}
}

detail视图的代码:

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 14的Group中的一个bug,即使使用Xcode 13构建也会发生。

发生的事情是SwiftUI运行时弄乱了它的视图差异,所以它认为它"出现"了。当它真的只是重新加载,因此没有消失(这是onAppear被调用的要求,因此是一个bug)。

为了弥补这个问题,您需要使用初始化器,而不是依赖于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
}
}
}

希望项目进展顺利!

我在iOS 15中看到了类似的东西,将所有的onAppear内容移到初始化器中修复了一些问题。没有更多的无限循环试图使对话框出现。奇怪的是,这只发生在特定状态的设置。

在onAppear中,我有这样的代码:
useSecondary = data.settings.showSecondaryAxis
...

但是在init函数中需要这个来完成工作:

init(data: Binding<PlotData> {
self._data = data
_useSecondary = State(initialValue: data.wrappedValue.settings.showSecondaryAxis)
... 

}

…很麻烦,但是很好用。

相关内容

  • 没有找到相关文章

最新更新