Reputation: 36003
I have an observable object called MyObject
that has a published property called property
. property
is String.
I have this
.onChange(of:myObject.property, perform: { value in
}
the problem is that value
contains the old value of property, when this is triggered.
Apple docs say you can capture the new and old value of properties by doing this:
@State private var playState: PlayState = .paused
.onChange(of: playState) { [playState] newState in
print(playState, self.playState, newState)
}
I don't see how I should type the command for my case.
I have tried
.onChange(of:myObject.property, perform: { [myObject.property] newValue in
but Xcode says
Expected 'weak', 'unowned', or no specifier in capture list
Any ideas?
Upvotes: 8
Views: 4139
Reputation: 278
I ran into the same issue. You can capture the old value of you model property by assigning it inside the capture brackets.
.onChange(of:myObject.property, perform: { [oldValue = myObject.property] newValue in
print("Old value was \(oldValue), new value is \(newValue)")
}
Upvotes: 18
Reputation: 1
I decided separate this 2 way, because it would make confusing of usage or using wrong code for wrong way.
Notice that you can use in this way as well, I used other way in body:
.performOnChange(of: model.randomElement, withKey: "WhiteRabbit") { value in
print("oldValue:", value.oldValue)
print("newValue:", value.newValue)
print("- - - - - - -")
}
import SwiftUI
struct ContentView: View {
@StateObject var model: Model = Model()
var body: some View {
VStack {
Text(model.randomElement)
.padding()
Button("shuffle") { model.randomElement = model.array[Int.random(in: model.array.indices)] }
}
.font(Font.headline)
.performOnChange(of: model.randomElement, withKey: "WhiteRabbit") { oldValue, newValue in // <<: Here! using: performOnChange
print("oldValue:", oldValue)
print("newValue:", newValue)
print("- - - - - - -")
}
}
}
class Model: ObservableObject {
let array: [String] = ["a", "b", "c", "d"]
@Published var randomElement: String = "No random Element!"
}
extension View {
func performOnChange<T: Equatable>(of inPutValue: T, withKey key: String, capturedValues: @escaping (ReturnValueType<T>) -> Void) -> some View {
return self
.preference(key: OptionalGenericPreferenceKey<T>.self, value: inPutValue)
.onPreferenceChange(OptionalGenericPreferenceKey<T>.self) { newValue in
if let unwrappedNewValue: T = newValue {
if let unwrappedOldValue: T = backupDictionary[key] as? T { capturedValues((oldValue: unwrappedOldValue, newValue: unwrappedNewValue)) }
}
backupDictionary[key] = newValue
}
}
}
typealias ReturnValueType<T: Equatable> = (oldValue: T, newValue: T)
var backupDictionary: [String: Any] = [String: Any]()
struct OptionalGenericPreferenceKey<T: Equatable>: PreferenceKey {
static var defaultValue: T? { get { return nil } }
static func reduce(value: inout T?, nextValue: () -> T?) { value = nextValue() }
}
Upvotes: 3
Reputation: 1
Here a working example of it, notice that onChange
would work if there is a change in value, otherwise will no react:
import SwiftUI
struct ContentView: View {
@StateObject var model: Model = Model()
@State private var randomElement: String = Model().randomElement
var body: some View {
VStack {
Text(model.randomElement)
.padding()
Button("shuffle") { model.randomElement = model.array[Int.random(in: model.array.indices)] }
}
.font(Font.headline)
.onChange(of: model.randomElement) { newValue in randomElement = newValue }
.onChange(of: randomElement) { [randomElement] newValue in
print("oldValue is:", randomElement)
print("newValue is:", newValue)
print("- - - - - - - - - - - - ")
}
}
}
class Model: ObservableObject {
let array: [String] = ["a", "b", "c", "d"]
@Published var randomElement: String = "No random Element!"
}
Upvotes: 1