如何在onAppear方法上有条件地执行代码



我有一个取决于类数据的swiftUi视图。在显示数据之前,我必须用.onAppear方法进行计算。

只有当我观察到的物体发生变化时,我才想进行这种繁重的计算。

问题是每次打开视图时都会调用.onPear,但对象值不会经常更改。

只有当观测到的数据被有效修改时,才有可能有条件地执行计算函数吗?

import SwiftUI
struct test2: View {
@StateObject var person = Person()
@State private var computedValue = 0

var body: some View {

List {
Text("age = (person.age)")
Text("computedValue = (computedValue)")
}
.onAppear {
computedValue = compute(person.age)     /// Executed much too often :(
}
}

func compute(_ age: Int) -> Int {
return age * 2  /// In real life, heavy computing
}
}
class Person: ObservableObject {
var age: Int = 0
}

谢谢你的建议:(

视图模型中的代码可能会少一些,但要在视图中进行所有计算,需要进行一些更改。首先,您的class Person没有@Published变量,因此它永远不会调用状态更改。我已经解决了。

其次,现在您的状态将更新,您可以将.onReceive()添加到视图中,以跟踪age何时更新。

第三,也是极其重要的一点,要防止用";"重计算";,您应该实现Async Await。因此,即使我在线程上睡了3秒钟,UI仍然是流畅的。

struct test2: View {
@StateObject var person = Person()
@State private var computedValue = 0

var body: some View {

List {
Text("age = (person.age)")
Text("computedValue = (computedValue)")
Button {
person.age = Int.random(in: 1...80)
} label: {
Text("Randomly Change Age")
}
}
// This will do your initial setup
.onAppear {
Task {
computedValue = await compute(person.age)     /// Executed much too often :(
}
}
// This will keep it current
.onReceive(person.objectWillChange) { _ in
Task {
computedValue = await compute(person.age)     /// Executed much too often :(
}
}
}

func compute(_ age: Int) async -> Int {
//This is just to simulate heavy work.
do {
try await Task.sleep(nanoseconds: UInt64(3.0 * Double(NSEC_PER_SEC)))
} catch {
//handle error
}
return age * 2  /// In real life, heavy computing
}
}
class Person: ObservableObject {
@Published var age: Int = 0
}

一个可能的解决方案是创建一个具有Bool值的EnvironmentObject,Change当对象中存在Change时,该变量的值。因此,只要检查environmentobject值是true还是false,它就会执行您的函数。希望你觉得这个有用。

task(id:priority:_:(就是这个问题的解决方案。

"当视图出现时异步运行指定操作的视图,或在id值发生更改时重新启动任务的视图">

id参数设置为要监视更改的数据。

我遇到了同样的问题。我使用.task视图函数来获取后端数据。问题是每次切换视图时都会执行.task。(我的应用程序有一个包含多个选项卡的TabView(。最简单的方法是引入@State变量来跟踪数据加载状态。以下是解决方案:

import SwiftUI
struct test2: View {
@StateObject var person = Person()
@State private var computedValue = 0
@State private var isDataLoaded = false

var body: some View {

List {
Text("age = (person.age)")
Text("computedValue = (computedValue)")
}
.onAppear {
if isDataLoaded {
return
} 
computedValue = compute(person.age)     /// Executed much too often :(
isDataLoaded = true
}
}

func compute(_ age: Int) -> Int {
return age * 2  /// In real life, heavy computing
}
}
class Person: ObservableObject {
var age: Int = 0
}

我不确定这是否是一个好的做法,但它以某种方式解决了我的问题。希望能帮助

引用:https://forums.swift.org/t/how-to-launch-effect-onapper-only-once/63455/4

最新更新