1

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()
      }
  }

2 Answers 2

4

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)
        }
    }
}
3
  • Wow, thank you for taking the time to look at this for me. I've been looking all over for this solution. I wish I could find a source for SwiftUI that explained stuff like that. There are sources for examples of how to do this stuff, but no one explains the nuances of the processes. I recently read "Mastering Swift 5.3" by Jon Hoffman which helped me tremendously with the Swift language. Would that there were a resource like it for SwiftUI.
    – rpetruzz
    Commented Mar 14, 2021 at 11:47
  • I highly recommend swiftui-lab.com and their companion app. Great resources. If this answer was helpful, feel free to upvote as well.
    – jnpdx
    Commented Mar 14, 2021 at 16:07
  • Hello, thank you AGAIN for reminding me about swift-lab.com. Actually I had purchased the companion app and forgot about it. I opened the app and used Javier's picker examples to try and found that they didn't work. I corresponded with Javier and he confirmed overnight that Apple changed the Picker behavior for optionals. Thanks again...
    – rpetruzz
    Commented Mar 16, 2021 at 11:45
1

Try to add .onChange:

Picker(selection: $selectedAccount, label: Text("")) {
    ForEach (self.acctRecords, id: \.self) { acct in
        Text("\(acct.account_code!)").tag(acct.account_code) // <- add tag here
    }
}
.onChange(of: selectedAccount) {
    selectedAccount = $0
}
1
  • Thank you, it was that tag mentioned above along with making sure that my tag type = the bound selection type that solved my problem. I did however add .onChanged to get some text to reflect the change.
    – rpetruzz
    Commented Mar 14, 2021 at 15:30

Not the answer you're looking for? Browse other questions tagged or ask your own question.