skip to Main Content

in this code I want to change two picker values when they are same to remain this two different from each other. I try to use swap but that’s not working properly.

for example : when first picker value is one and second is two, when I try to change second value to "one", value immediately change to one and first picker become two!

 struct ContentView: View {
    @State private var list = ["one","two","three","four","five"]
    @State private var from: String = "one"
    @State private var from: String = "two"
         var body: some View {
       VStack {
          Picker("",selection: $from) {
             ForEach(list, id: .self) { item in
                Text(item)
         }
       }
       .pickerStyle(.menu)
       .padding(.trailing)
       .onChange(of: from) { newValue in
                                    if newValue == to {
                                        (to,from) = (from, to)
                                    }
                                }  

          Picker("",selection: $to) {
             ForEach(list, id: .self) { item in
                Text(item)
         }
       }
       .pickerStyle(.menu)
       .padding(.trailing)
    }
    }
}

I’m using this tuple solution for another section of the app but in this part that’s not working :

(from, to) = (to, from) 

how can I perform immediately swap this two values when for example to going to the same with from?

2

Answers


  1. still not 100% sure what you are trying to do, but it may be something like this,
    as shown in this example code using helper vars:

    struct ContentView: View {
        @State private var list = ["one","two","three","four","five"]
        
        @State private var from: String = "one"
        @State private var to: String = "two"
        
        @State private var prevFrom: String = "one"
        @State private var prevTo: String = "two"
        
        var body: some View {
            VStack {
                Picker("",selection: $from) {
                    ForEach(list, id: .self) { item in
                        Text(item)
                    }
                }
                .pickerStyle(.menu)
                .padding(.trailing)
                
                .onChange(of: to) { _ in
                    if to == from {
                        from = prevTo
                    }
                    prevTo = to
                }
                
                .onChange(of: from) { _ in
                    if to == from {
                        to = prevFrom
                    }
                    prevFrom = from
                }
                
                Picker("",selection: $to) {
                    ForEach(list, id: .self) { item in
                        Text(item)
                    }
                }
                .pickerStyle(.menu)
                .padding(.trailing)
            }
        }
    }
    
    Login or Signup to reply.
  2. I think there is a connection that is missing. You state

    If my change in first picker cause two picker become equal, so immediately swap them!

    If you swap 2 equal values you get 2 equal values.

    Assumption:

    What It seems you want is a solution for the standard interview question of swapping 2 values.

    Given x=1 and y=2. Swap the values

    The way to do this is

    let temp = y //holds a value
    y = x //starts swap
    x = temp //finishes swap
    

    Now your result is y=1 and x=2

    You need that temp variable to hold the value of one of them, while you are swapping values.

    Now, if we ge back to your statement, the fallacy is in waiting for the values to become equal.

    When you click on the new item the change is being made immediately, there is no way to retrieve what the value was previously.

    x = newValue or y = newValue
    

    The only way to achieve a "swapping" is to determine if there is a need to swap before the "source of truth" changes.

    1. Pick new value
    2. Check if swapping is needed
    3. Swap the source of truth values
    4. Tell SwiftUI there is an update

    You can achieve this setup in SwiftUI with an ObservableObject

    class AutoPickerVM: ObservableObject{
        ///Variable that holds the value for From - Source of truth
        private var storeFrom: String = "one"
        ///Processes the changes between the pickers and triggers view updates
        var from: String {
            get{
                storeFrom
            }
            set{
                if newValue == to{
                    //Take the value that is currently there
                    let curTo = to
                    //Start swapping
                    storeTo = storeFrom
                    //Place the previous value
                    storeFrom = curTo
                    
                }else{
                    storeFrom = newValue
                }
                objectWillChange.send()
            }
        }
        ///Variable that holds the value for to - Source of truth
        private var storeTo: String = "two"
        ///Processes the changes between the pickers and triggers view updates
        var to: String {
            get{
                storeTo
            }
            set{
                if newValue == from{
                    //Take the value that is currently there
                    let curFrom = from
                    //Start swapping
                    storeFrom = storeTo
                    //Place the previous value
                    storeTo = curFrom
                }else{
                    storeTo = newValue
                }
                
                objectWillChange.send()
            }
        }
        init(){}
    }
    

    Then your View would look like this

    struct AutoPicker: View {
        @StateObject var vm: AutoPickerVM = .init()
        @State private var list = ["one","two","three","four","five"]
        
        var body: some View {
            VStack {
                Picker("",selection: $vm.from) {
                    ForEach(list, id: .self) { item in
                        Text(item)
                    }
                }
                .pickerStyle(.menu)
                .padding(.trailing)
                
                Picker("",selection: $vm.to) {
                    ForEach(list, id: .self) { item in
                        Text(item)
                    }
                }
                .pickerStyle(.menu)
                .padding(.trailing)
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search