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?
CodePudding user response:
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.
- Pick new value
- Check if swapping is needed
- Swap the source of truth values
- 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)
}
}
}
CodePudding user response:
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)
}
}
}