Search code examples
swiftuiselectionpicker

Picker selection not updating


I'm trying to select a default account number from a list of available accounts. The data is from an FMDB data selection. I've created a test view with two types of pickers in it. One that lists the accounts I've retrieved into an array of data records which is a swift struct. The other picker is one that comes from an on-line example to select colors. The colors "selection" bound value updates as expected, but the "selection" bound value that I set does not change when one of two accounts is presented and selected. Below is the code from my test view that compiles and runs. When I select either account value which is [12345678] or [12345679] which appear as two rows in the picker the selection binding value doesn't change. But for the colors selection value it updates. I'm pretty confused here...

The struct for the accounts record is:

    // Account record for FMDB
    struct AccountRecord: Hashable {
        var account_id: Int!
        var account_code: Int!
        var account_name: String!
        var running_balance: Double!
        var hidden_balance: Double!
        var actual_balance: Double!
    }
  import SwiftUI

  struct PickerTestView: View {
    
    @State private var selectedAccount = 0
    @State private var selectedColor = 0
    
    var acctRecords: [Accounts.AccountRecord] {
        return Accounts.shared.selectAllAccounts()
    }
    var colors = ["Red", "Green", "Blue", "Tartan"]
    
    var body: some View {
        VStack{
            Picker(selection: $selectedAccount, label: Text(""))
            {
                ForEach (self.acctRecords, id: \.self) { acct in
                    Text("\(acct.account_code!)")
                }
            }
            
            Text("selectedAccount = \(selectedAccount)")
                .font(.largeTitle)
            
            Picker(selection: $selectedColor, label: Text("Please choose a color")) {
                ForEach(0 ..< colors.count) {
                    Text(self.colors[$0])
                }
            }
            
            Text("Selectedcolor = \(selectedColor)")
            Text("You selected \(colors[selectedColor])")
            
            
        }
    }
  }

  struct PickerTestView_Previews: PreviewProvider {
      static var previews: some View {
          PickerTestView()
      }
  }

Solution

  • Two things are happening:

    1. You need a .tag() on your Picker elements to tell the system which element belongs to what item:
    Picker(selection: $selectedAccount, label: Text(""))
                {
                    ForEach (self.acctRecords, id: \.self) { acct in
                        Text("\(acct.account_code!)").tag(acct.account_id)
                    }
                }
    
    1. SwiftUI needs the types of the selection parameter and the tag type to be the same. Because in your model, account_id is defined as Int! and not Int, your selectedAccount needs to be Int! as well:
    @State private var selectedAccount : Int! = 0 
    

    The following works with some test data embedded in:
    struct PickerTestView: View {
        @State private var selectedAccount : Int! = 1
        @State private var selectedColor = 0
        
        var acctRecords: [AccountRecord] {
            return [.init(account_id: 1, account_code: 1, account_name: "1", running_balance: 0, hidden_balance: 0, actual_balance: 0),
                    .init(account_id: 2, account_code: 2, account_name: "2", running_balance: 0, hidden_balance: 0, actual_balance: 0),
                    .init(account_id: 3, account_code: 3, account_name: "3", running_balance: 0, hidden_balance: 0, actual_balance: 0)
            ]
        }
        var colors = ["Red", "Green", "Blue", "Tartan"]
        
        var body: some View {
            VStack{
                Picker(selection: $selectedAccount, label: Text(""))
                {
                    ForEach (self.acctRecords, id: \.self) { acct in
                        Text("\(acct.account_code!)").tag(acct.account_id)
                    }
                }
                Text("selectedAccount = \(selectedAccount)")
                    .font(.largeTitle)
            }
        }
    }