6

I am using Picker with option for no selection, in iOS15 it is working fine, but in iOS16 it has a default value, how can I remove this default value, I don't need to show the text to the right of the Picker line when selection is nil.

struct ContentView: View {
    
    @State private var selection: String?
    let strengths = ["Mild", "Medium", "Mature"]
    
    var body: some View {
        NavigationView {
            List {
                Section {
                    Picker("Strength", selection: $selection) {
                        ForEach(strengths, id: \.self) {
                            Text($0).tag(Optional($0))
                        }
                    }
                }
            }
        }
    }
}

in iOS15, when selection is nil, no text is displayed on the right side of the Picker row
enter image description here

but in iOS 16, the same code leads to different results, when selection is nil it has a default value enter image description here

GorCat
  • 174
  • 3
  • 11

3 Answers3

15

Xcode 14.1 Beta 3 logs: "Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results."

To resolve this log you need to add an Option which uses the nil tag.

struct ContentView: View {

    @State private var selection: String?
    let strengths = ["Mild", "Medium", "Mature"]

    var body: some View {
        NavigationView {
            List {
                Section {
                    Picker("Strength", selection: $selection) {
                        Text("No Option").tag(Optional<String>(nil))
                        ForEach(strengths, id: \.self) {
                            Text($0).tag(Optional($0))
                        }
                    }
                    Text("current selection: \(selection ?? "none")")
                }
            }
        }
    }
}
Carsten
  • 581
  • 4
  • 17
4

This is what I ended up doing for iOS 16.0 from XCode 14.0.1 (to avoid user irritation on iOS 16.0 devices):

let promptText: String = "select" // just a default String
    
//short one for your example
Section {
    Picker("Strength", selection: $selection) {
        if selection == nil { // this will work, since there is no initialization to the optional value in your example
            Text(promptText).tag(Optional<String>(nil)) // is only shown until a selection is made
        }
        ForEach(strengths, id: \.self) {
            Text($0).tag(Optional($0))
        }
    }
}
    
// more universal example
Section {
    Picker("Strength", selection: $selection) {
        if let safeSelection = selection{
            if !strengths.contains(safeSelection){ // does not care about a initialization value as long as it is not part of the collection 'strengths'
                Text(promptText).tag(Optional<String>(nil)) // is only shown until a selection is made
            }
        }else{
            Text(promptText).tag(Optional<String>(nil))
        }
        ForEach(strengths, id: \.self) {
            Text($0).tag(Optional($0))
        }
    }
}
    
// Don't want to see anything if nothing is selected? empty String "" leads to an warning. Go with non visual character like " " or 'Horizontal Tab'. But then you will get an empty row...
Section {
    let charHorizontalTab: String = String(Character(UnicodeScalar(9)))
    Picker("Strength", selection: $selection) {
        if let safeSelection = selection{
            if !strengths.contains(safeSelection){ // does not care about a initialization value as long as it is not part of the collection 'strengths'
                Text(charHorizontalTab).tag(Optional<String>(nil)) // is only shown until a selection is made
            }
        }else{
            Text(charHorizontalTab).tag(Optional<String>(nil))
        }
        ForEach(strengths, id: \.self) {
            Text($0).tag(Optional($0))
        }
    }
}

good luck finding a solution that works for you

0

This is how I solved it:

@State private var showAddNewUserWindow = false
@State private var newUser: String = ""

@State private var selectedUser = "Item 1"
@State private var usersList = ["Item 1", "Item 2", "Item 3", "Item 4"]
@State private var isPickerDisabled = false

Button("—") {
    if let index = usersList.firstIndex(of: selectedUser) {
        usersList.remove(at: index)
        if usersList.count == 0 {
            usersList.insert("No user", at: 0)
            isPickerDisabled = true
        }
        selectedUser = usersList.first!
    }
}
                
Picker("",selection:$selectedUser) {
    ForEach(usersList, id: \.self) {
        Text($0)
    }
                    
}
.disabled(isPickerDisabled)
.onChange(of: selectedUser) {_ in
    usersList.remove(at: usersList.firstIndex(of: selectedUser)!)
    usersList.insert(selectedUser, at: 0)
}
                
Button("+") {
    showAddNewUserWindow = true
}
.popover(isPresented: $showAddNewUserWindow) {
    VStack {
        Spacer()
            .frame(height: 26.0)
                        
        Text("Add new user")
            .font(.title2)
                        
        Spacer()
            .frame(height: 6.0)
                        
        Text("Please enter the name of the new user")
            .font(.caption)
                        
        Spacer()
            .frame(height: 20.0)
                        
        HStack {
            Spacer()
                .frame(width: 24.0)
                            
            TextField("New user's name", text: $newUser)
                .textFieldStyle(.roundedBorder)
                            
            Spacer()
                .frame(width: 24.0)
        }
                        
        Spacer()
            .frame(height: 20.0)
                        
        HStack {
            Button("Cancel") {
                showAddNewUserWindow.toggle()
            }
            .frame(width: 145.0, height: 50.0)
            .border(.quaternary, width: 0.8)
                            
            Button("Add") {
                if newUser.count > 0 {
                    if usersList.contains(newUser) == false {
                        if usersList.first == "No user" {
                            usersList.removeFirst()
                            isPickerDisabled = false
                        }
                                        
                        usersList.insert(newUser, at: 0)
                        selectedUser = newUser
                        newUser = ""
                        showAddNewUserWindow.toggle()
                    } else {
                                        
                    }
                } else {
                                    
                }
            }
            .frame(width: 145.0, height: 50.0)
            .border(.quaternary, width: 0.8)
        }
    }        
}
Xiaomu Gu
  • 11
  • 2