简体   繁体   中英

Why is didSet not working as expected with a Binding property in SwiftUI

I have the following two properties in a child view:

  1. is a local @State var localValue that is initialized to false
  2. is a @Binding var parentValue property that is being toggled by a parent view.

I want to use the localValue in my code since it starts out false and then is toggled to true when the view is initialized and this starts an animation that needs to start out as false. The parentValue property gets toggled in the parent view and I want this to update my localValue property to control the animation properly. Although the parentValue is indeed toggling the localValue property does not seem to be changing with the didSet. What am I missing???

@State var localValue = false

@Binding var parentValue: Bool {
    didSet {localValue = parentValue}
}

Update with more detail based on comment

If there is no way to do this because the @Binding wrapper itself is not changing so didSet is not being triggered then what I need is similar.

I would like to have a property that is initially set to false but that can be toggled from an external view so as to control an animation in the child view which changes direction based on the value being toggled.

If I could initialize the bound property in the child view to false ie if there was some way to set

@Binding var breath: Bool = false 

as the starting value then each time the parent toggles the parentValue property the child view would respond properly. As it is now the parent view calls the child view with this property set to true which means the animation is fully expanded whereas I want the first step to have the animation grow for a particular duration and then reverse when the parent sets the value to false for some other duration amount.

    // Whole flower
                .rotationEffect(.degrees(breath ? 360 : 0), anchor: .center) // Inhale = clockwise rotation, Exhale = anticlockwise rotation
                .scaleEffect(breath ? 1 : 0.2) // Inhale = upscale, Exhale = downscale
                .animation( Animation.easeInOut(duration: self.stepDuration))
                .opacity(breath ? 1 : 0.75)

See the code above for why it is important that when the view starts the animation is at a scale factor of 0.2 but then grows to full size before reversing when the duration has expired and the breath property has been toggled to false.

The reason didSet is not called is because the property's value is not changed. You would have to assign a new Binding to the underlying _parentValue property. Instead, you can define a new Binding property that mutates the parentValue property and calls a custom closure. An easy way to do this is to define an extension method like this:

extension Binding {
    func didSet(_ closure: @escaping (Value) -> ()) -> Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: {
                self.wrappedValue = $0
                closure($0)
            }
        )
    }
}

Then when you initialize your property, you can do something like:

self.parentValue = $someBinding.didSet { localValue = $0 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM