Erich Vogel
Erich Vogel

Reputation: 55

How to correctly handle Picker in Update Views (SwiftUI)

I'm quite new to SwiftUI and I'm wondering how I should use a picker in an update view correctly.

At the moment I have a form and load the data in with .onAppear(). That works fine but when I try to pick something and go back to the update view the .onAppear() gets called again and I loose the picked value.

In the code it looks like this:

import SwiftUI

struct MaterialUpdateView: View {
    
    // Bindings
    @State var material: Material
    
    // Form Values
    @State var selectedUnit = ""
    
    var body: some View {

        VStack(){
            List() {
                Section(header: Text("MATERIAL")){

                    // Picker for the Unit
                    Picker(selection: $selectedUnit, label: Text("Einheit")) {
                        ForEach(API().units) { unit in
                            Text("\(unit.name)").tag(unit.name)
                        }
                    }

                }
            }
            .listStyle(GroupedListStyle())
            
        }
        .onAppear(){
            prepareToUpdate()
        }
    }

    func prepareToUpdate() {
        self.selectedUnit = self.material.unit
    }
}

Does anyone has experience with that problem or am I doing something terribly wrong?

Upvotes: 1

Views: 319

Answers (1)

BiOS
BiOS

Reputation: 2304

You need to create a custom binding which we will implement in another subview. This subview will be initialised with the binding vars selectedUnit and material

First, make your MaterialUpdateView:

struct MaterialUpdateView: View {
    
    
    // Bindings
    @State var material : Material
    
    // Form Values
    @State var selectedUnit = ""
    
    
    var body: some View {
        
        NavigationView {
            
            VStack(){
                List() {
                    Section(header: Text("MATERIAL")) {
                        
                        MaterialPickerView(selectedUnit: $selectedUnit, material: $material)
                        
                    }
                    .listStyle(GroupedListStyle())
                }
                .onAppear(){
                    prepareToUpdate()
                    
                }
                
            }
        }
        
    }
    
    func prepareToUpdate() {
        self.selectedUnit = self.material.unit
    }
    
}

Then, below, add your MaterialPickerView, as shown:

Disclaimer: You need to be able to access your API() from here, so move it or add it in this view. As I have seen that you are re-instanciating it everytime, maybe it is better that you store its instance with let api = API() and then refer to it with api, and even pass it to this view as such!

struct MaterialPickerView: View {
    
    @Binding var selectedUnit: String
    @Binding var material : Material
    @State var idx: Int = 0
        
    var body: some View {
        
        let binding = Binding<Int>(
            get: { self.idx },
            set: {
                self.idx = $0
                self.selectedUnit = API().units[self.idx].name
                self.material.unit = self.selectedUnit
            })
        
        return Picker(selection: binding, label: Text("Einheit")) {
            ForEach(API().units.indices) { i in
                Text(API().units[i].name).tag(API().units[i].name)
            }
        }
    }
}

That should do,let me know if it works!

Upvotes: 1

Related Questions