我有一个取决于类数据的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