在 SwiftUI 中使用 onAppear 时无限循环

Infinite loop when using onAppear in SwiftUI

我在使用 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 14 with Group 中的错误,即使在使用 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
        }
    }
}

希望项目顺利!

我看到了与 iOS 15 类似的内容,并将所有 onAppear 内容移至初始化程序修复的内容。 不再无限循环试图使对话框出现。奇怪的是,这只发生在特定状态下的设置。

在 onAppear 中我有这样的代码:

useSecondary = data.settings.showSecondaryAxis
...

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

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

}

… 麻烦但好用。