skip to Main Content

I am unable to use the EquatableView as per the article: Link1 and link2. I expected the "==" function in the TestView to be invoked every time I click on the Button.

Am I missing something here?

struct ContentView: View {   
    @State var state = false   
    var body: some View {
        VStack {
            EquatableView(content: TestView(state: $state))
            Button("Change state", action: {state.toggle()})
        }
    }
}

struct TestView: View, Equatable {
    @Binding var state: Bool
    var body: some View {
        let _ = print("Test updated")
        Text("TestView state : (state.description)")
    }
    static func == (lhs: TestView, rhs: TestView) -> Bool {
        //Never printed or invoked
        let _ = print ("lhs == rhs invoked  (lhs.state) == (rhs.state)") 
        return lhs.state == rhs.state
    }
}

UPDATE:

After very helpful response from Alperen, it shows that “@State” has to be added for == to be triggered. However, passing a "@State" var from parent directly to SubView’s "@State" for the same var, as suggested below, makes no sense.

So, I added another variable to TestView “@State var notUsed“ which will not work unless "@Binding" is removed. One would also want to compare "@Binding" vars, so to me this is illogical.

My question still remains, am I missing something or is this behaviour by design?

2

Answers


  1. That code is working. Can you try this?

    SS

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    
    
    struct ContentView: View {
        @State var state = false
        var body: some View {
            VStack {
                TestView(state: state).equatable()
                Button("Not Change", action: {})
                Button("Change", action: {state.toggle()})
               
            }
        }
    }
    
    
    struct TestView: View, Equatable {
        @State var state: Bool
        var body: some View {
            let _ = print("Test updated")
            Text("TestView state : (state.description)")
        }
        static func == (lhs: TestView, rhs: TestView) -> Bool {
            //Never printed or invoked
            let _ = print ("lhs == rhs invoked  (lhs.state) == (rhs.state)")
            return lhs.state == rhs.state
        }
    }
    
    Login or Signup to reply.
  2. Equatable is used when a view is created, so either it should replace already existed or not. In your example TestView is not re-created, because it should not on binding changed – only body rendered with new value.

    To simulate recreation (for testing purpose) the simplest it to add condition. Equatibility is just a optimisation technic it helps SwiftUI to detect replace existed view instance or not.

    Here is updated example to demo Equatable works. Tested with Xcode 12.1 / iOS 14.1

    struct ContentView: View {
        @State var state = false
        var body: some View {
            VStack {
                if state {
                   EquatableView(content: TestView(state: $state))
                }
                Button("Change state", action: {state.toggle()})
            }
        }
    }
    
    struct TestView: View, Equatable {
        @Binding var state: Bool
        init(state: Binding<Bool>) {
            _state = state
            print(">> inited")        // << check if created
        }
        var body: some View {
            let _ = print("Test updated")
            Text("TestView state : (state.description)")
        }
        static func == (lhs: TestView, rhs: TestView) -> Bool {
            //Never printed or invoked
            let _ = print ("lhs == rhs invoked  (lhs.state) == (rhs.state)")
            return lhs.state == rhs.state
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search