在 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)
...
}
…
麻烦但好用。
我在使用 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)
...
}
… 麻烦但好用。