How can I change a part of a view after a picker selection?

Good morning,

I would like to ask a question about a small problem that I am facing with.
I would like to change a part of the view after a picker selection and the only way comes to my mind is an if chain to check the selected value, according to .tag() modifier. In this way, code is very dirty and complex.
Is there an easy way to do that?

Answered by OOPer in 619046022

I would like to do something like this. 

Thanks for showing your code.
One question for you, even if Swift accepted this sort of declaration, you need to update selectedView, maybe using if-chain or switch.
Is that acceptable for you?

The only way that comes to my mind is the following. 

Exactly the same as I had in mind, which is the only way I can think of.
In Swift 5.3/Xcode 12, you can replace if-chain to switch, but you would think those are equivalent.

If the subviews were only to be displayed I could use an Anyview array, but I need to access to the subviews state. 

Seems you have more ways to work with SwiftUI than me.

But if you need to access to the subviews state, why don't you make such states @Binding and declare actual @State somewhere else?
It is not clear why you need .tag() to change a part of a view. Can you create a minimized project to reproduce the issue and show whole code?

I want to choose different subview depending on the selected item in the picker.
Here there is an example.

Code Block
struct FirstView: View{
@State var selectedValue = 0
var body: some View{
VStack{
Picker(selection: $selectedValue, label: Text("someText")){
                    Text("A").tag(0)
                    Text("B").tag(1)
                    Text("C").tag(2)
                    Text("D").tag(3)
                    Text("E").tag(4)
                    Text("F").tag(5)
                }.pickerStyle(SegmentedPickerStyle())
// Here I want to choose different views depending on the item selected
}
}
}



Sorry, but where is different subview to show?
Here I add it.

Code Block
struct FirstView: View{
@State var selectedValue = 0
var body: some View{
VStack{
Picker(selection: $selectedValue, label: Text("someText")){                   
Text("A").tag(0)                   
Text("B").tag(1)                   
Text("C").tag(2)                   
Text("D").tag(3)                   
Text("E").tag(4)                   
Text("F").tag(5)               
}.pickerStyle(SegmentedPickerStyle())
/* Here I want to choose different views depending on the item selected */
}
}
}
struct SubviewA: View{
var body: some View{
Text("Subview A")
}
}
struct SubviewB: View{
var body: some View{
Text("Subview B")
}
}
/* One subview for each item in picker */


There must be one view for each item in picker. In the real scenario, each subview is more complex than a simple Text and has different image and different components. When the selected item changes, the corresponding subview must be displayed.

In that way I can do a sort of "callback", but I can't add add or change anything in the body.
@mirkofranco 

Here I add it. 

Sorry, but I was not clear enough in my last reply.

You once said,

the only way comes to my mind is an if chain to check the selected value, according to .tag() modifier. In this way, code is very dirty and complex.

Please show that code you think is dirty and complex, even if it does not compile.

In my opinion, that might be the only solution in your case, and I do not want to show duplicate code which you think very dirty.
I would like to do something like this.

Code Block
struct FirstView: View{
@State var selectedValue = 0
var selectedView : some View = SubviewA()
var body: some View{
VStack{
Picker(selection: $selectedValue, label: Text("someText")){
Text("A").tag(0)                   
Text("B").tag(1)                   
Text("C").tag(2)                   
Text("D").tag(3)                   
Text("E").tag(4)                   
Text("F").tag(5)               
}.pickerStyle(SegmentedPickerStyle())
selectedView
}
}
}
struct SubviewA: View{
var body: some View{
Text("Subview A")
}
}
struct SubviewB: View{
var body: some View{
Text("Subview B")
}
}
struct SubviewC: View{
var body: some View{
Text("Subview C")
}
}
struct SubviewD: View{
var body: some View{
Text("Subview D")
}
}
struct SubviewE: View{
var body: some View{
Text("Subview E")
}
}
struct SubviewF: View{
var body: some View{
Text("Subview F")
}
}

In the real scenario each subview is more complex than a simple Text. It has an image and a Picker, like this.

Code Block
struct SubviewRealExample: View {
    @State var selection = [Int](repeatElement(0, count: 2))
    var body: some View{
        VStack{
            ExercisePreview(background: "bgAcuity_thumb", symbol: ViewModel.getImageName())
            MultiPicker(data: ViewModel.getData(), selection: $selection)
        }
    }
}

I would like selectedView to change every time the selected item in the picker changes. The only way that comes to my mind is the following.

Code Block
struct FirstView: View{
@State var selectedValue = 0
var body: some View{
VStack{
Picker(selection: $selectedValue, label: Text("someText")){
Text("A").tag(0)                   
Text("B").tag(1)                   
Text("C").tag(2)                   
Text("D").tag(3)                   
Text("E").tag(4)                   
Text("F").tag(5)               
}.pickerStyle(SegmentedPickerStyle())
if (selectedValue == 0){
SubviewA()
}else if(selectedValue == 1){
SubviewB()
}else if(selectedValue == 2){
SubviewC()
}else if(selectedValue == 3){
SubviewD()
}else if(selectedValue == 4){
SubviewE()
}else if(selectedValue == 5){
SubviewE()
}else{
EmptyView()
}
}
}
}
struct SubviewA: View{
var body: some View{
Text("Subview A")
}
}
struct SubviewB: View{
var body: some View{
Text("Subview B")
}
}
struct SubviewC: View{
var body: some View{
Text("Subview C")
}
}
struct SubviewD: View{
var body: some View{
Text("Subview D")
}
}
struct SubviewE: View{
var body: some View{
Text("Subview E")
}
}
struct SubviewF: View{
var body: some View{
Text("Subview F")
}
}


If the subviews were only to be displayed I could use an Anyview array, but I need to access to the subviews state.




Accepted Answer

I would like to do something like this. 

Thanks for showing your code.
One question for you, even if Swift accepted this sort of declaration, you need to update selectedView, maybe using if-chain or switch.
Is that acceptable for you?

The only way that comes to my mind is the following. 

Exactly the same as I had in mind, which is the only way I can think of.
In Swift 5.3/Xcode 12, you can replace if-chain to switch, but you would think those are equivalent.

If the subviews were only to be displayed I could use an Anyview array, but I need to access to the subviews state. 

Seems you have more ways to work with SwiftUI than me.

But if you need to access to the subviews state, why don't you make such states @Binding and declare actual @State somewhere else?
Thanks for your answer.
No, my goal was not to insert if chain, but this seems to be the only way.
I use @Binding and @State to access the subviews state.
I thought there was something like this, as in other languages, so I could have had a cleaner code and take advantage of the polymorphism.
Code Block
var selectedView : any View
/* or something like this */
var viewList : [View]


Not sure if you still have this issue, but I'll share my findings.

I'm facing a similar issue and have to display different fields based on what the picker selection is. May not be the best approach, but it worked for me. I've been learning swift for a couple of weeks.

You can use a function that returns a view. Pass the selection result to that function and use a switch case to return what you need. You can also pass additional binding parameters to that function.

var body: some View {
    Picker(selection: $selectedOption, label: Text("mylabel")) {
        Text("option A").tag(OptionEnum.A)
        Text("option B").tag(OptionEnum.B)
    }.pickerStyle(.segmented)

    displayCorrectView(selectedOption)
}

func displayCorrectView(_ selectedOption: OptionEnum) -> some View {
    switch selectedOption {
    case .A:
        return Text("selectedA")
    case .B:
        return Text("selectedB")
    }
}

I've not compiled this exact piece of code, but a very similar one instead.

If there's a better approach you can share it with me.

How can I change a part of a view after a picker selection?
 
 
Q