Given the setup I've outlined below, I'm trying to determine why ChildView
's .onChange(of: _)
is not receiving updates.
import SwiftUI
struct SomeItem: Equatable {
var doubleValue: Double
}
struct ParentView: View {
@State
private var someItem = SomeItem(doubleValue: 45)
var body: some View {
Color.black
.overlay(alignment: .top) {
Text(someItem.doubleValue.description)
.font(.system(size: 50))
.foregroundColor(.white)
}
.onTapGesture { someItem.doubleValue += 10.0 }
.overlay { ChildView(someItem: $someItem) }
}
}
struct ChildView: View {
@StateObject
var viewModel: ViewModel
init(someItem: Binding<SomeItem>) {
_viewModel = StateObject(wrappedValue: ViewModel(someItem: someItem))
}
var body: some View {
Rectangle()
.fill(Color.red)
.frame(width: 50, height: 70, alignment: .center)
.rotationEffect(
Angle(degrees: viewModel.someItem.doubleValue)
)
.onTapGesture { viewModel.changeItem() }
.onChange(of: viewModel.someItem) { _ in
print("Change Detected", viewModel.someItem.doubleValue)
}
}
}
@MainActor
final class ViewModel: ObservableObject {
@Binding
var someItem: SomeItem
public init(someItem: Binding<SomeItem>) {
self._someItem = someItem
}
public func changeItem() {
self.someItem = SomeItem(doubleValue: .zero)
}
}
Interestingly, if I make the following changes in ChildView
, I get the behavior I want.
Change
@StateObject
to@ObservedObject
Change
_viewModel = StateObject(wrappedValue: ViewModel(someItem: someItem))
toviewModel = ViewModel(someItem: someItem)
From what I understand, it is improper for ChildView
's viewModel
to be @ObservedObject
because ChildView
owns viewModel
but @ObservedObject
gives me the behavior I need whereas @StateObject
does not.
Here are the differences I'm paying attention to:
- When using
@ObservedObject
, I can tap the black area and see the changes applied to both the white text and red rectangle. I can also tap the red rectangle and see the changes observed inParentView
through the white text. - When using
@StateObject
, I can tap the black area and see the changes applied to both the white text and red rectangle. The problem lies in that I can tap the red rectangle here and see the changes reflected inParentView
butChildView
doesn't recognize the change (rotation does not change and "Change Detected" is not printed).
Is @ObservedObject
actually correct since ViewModel
contains a @Binding
to a @State
created in ParentView
?
Item
aclass
orstruct
? What is.onItemChanged
? Can you show the code for theUnrelatedView
s so that we can have a compilable minimal reproducible example? – Radarman@Binding
outside of aView
, like you are here in yourViewModel
, you're making an odd semantic decision, as describe in my answer to your last question. – Radarman@Published
would be the way recommended by Apple for use in yourViewModel
class. – Dartmouth@Binding
in the view model is odd as you suggested in my other question but the code in this question was actually written before the other and I want to get to the bottom of the issue here before I consider changing@Binding
toBinding<SomeItem>
. – Veasey@Published
before posting and that gave into some read/write access issues. – Veasey