17.8 C
New York
Sunday, September 8, 2024

ios – SwiftUI customized Binding inflicting sudden rendering


Whereas investigating efficiency points in an app I am engaged on, I observed {that a} view that incorporates a customized binding will all the time be rendered when its father or mother is rendered, even when the state that triggered the father or mother to render has nothing to do with the binding itself. Instance beneath:

struct Father or mother: View {
    @State var textual content = "textual content"
    @State var bool = false

    var physique: some View {
        let _ = Self._printChanges()
        VStack {
            CustomBindingChild(worth: Binding(
                get: {
                    textual content
                },
                set: { newValue in
                    textual content = newValue
                }
            ))
            BindingChild(worth: $textual content)
            Button("Toggle bool") {
                bool.toggle()
            }
            Button("Change textual content") {
                textual content = textual content == "textual content" ? "textual content 2" : "textual content"
            }
        }
    }
}

struct CustomBindingChild: View {
    @Binding var worth: String

    var physique: some View {
        let _ = Self._printChanges()
        Textual content(worth)
    }
}

struct BindingChild: View {
    @Binding var worth: String

    var physique: some View {
        let _ = Self._printChanges()
        Textual content(worth)
    }
}

With the setup above, in the event you faucet the “Change textual content” button all three views are printed, as I’d count on. In the event you faucet the “Toggle bool” button, then again, the father or mother view and the customized binding view are printed and I count on solely the father or mother to take action.
To avoid this problem I created my very own implementation of what I am calling a StableBinding:

@propertyWrapper
struct StableBinding: Equatable {
    typealias Getter = () -> Worth
    typealias Setter = (Worth) -> Void

    personal var getter: Getter
    personal var setter: Setter

    var wrappedValue: Worth {
        get { return getter() }
        nonmutating set { self.setter(newValue) }
    }

    var projectedValue: Self { self }

    init(get: @escaping Getter, set: @escaping Setter) {
        self.getter = get
        self.setter = set
    }

    init(_ binding: Binding) {
        self.getter = { binding.wrappedValue }
        self.setter = { newValue in
            binding.wrappedValue = newValue
        }
    }

    static func == (lhs: StableBinding, rhs: StableBinding) -> Bool {
        lhs.getter() == rhs.getter()
    }
}

The truth that it implements the Equatable protocol ensures that it does not trigger the view that is utilizing it to be rendered each time it is evaluated.
My questions are two:

  1. Is there a purpose behind Swift’s customized binding behaves like this?
  2. Is there an issue in my strategy? I’ve examined it and could not discover any however it will be actually disappointing to search out out that I missed one thing apparent after it hits manufacturing.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles