【问题标题】:Infinite loop when using onAppear in SwiftUI在 SwiftUI 中使用 onAppear 时出现无限循环
【发布时间】: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,它可以工作。所以,这是一个苹果虫。

标签: swiftui combine


【解决方案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)
... 

}

… 很麻烦,但很管用。

【讨论】:

    猜你喜欢
    • 2021-01-09
    • 2021-07-25
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    • 2021-11-20
    • 2022-01-19
    • 2023-03-16
    相关资源
    最近更新 更多