I have a SwiftUI list, defined in a typical fashion:
struct SettingsView: View
{
@State private var selectedCategory: SettingsCategory? = .general
List(SettingsCategory.allCases, id: \.self, selection: $selectedCategory) { category in
[...]
}
}
In this case, the List
is a table of "categories" for a settings area in my UI. The SettingsCategory
is an enum that defines these categories, and the UI ends up looking like this:
It is not appropriate for this list to have an empty selection; a category should always be selected. In AppKit, it was trivially easy to disable an empty selection on NSTableView
. But in SwiftUI, I've been unable to find a way to disable it. Anytime I click in the empty area of the list, the selection is cleared. How can I stop that?
selectedCategory
must be an Optional or the compiler vomits all over itself.
I can't use willSet/didSet
on selectedCategory
because of the @State
property wrapper. And I can't use a computed property that never returns nil because the List
's selection has to be bound.
I also tried this approach: SwiftUI DatePicker Binding optional Date, valid nil
So, what magical incantation is required to disable empty selection in List
?
CodePudding user response:
One solution would be to set the selection back to the original one if the selection becomes nil
.
Code:
struct ContentView: View {
@State private var selectedCategory: SettingsCategory = .general
var body: some View {
NavigationView {
SettingsView(selectedCategory: $selectedCategory)
Text("Category: \(selectedCategory.rawValue.capitalized)")
.navigationTitle("App")
}
}
}
enum SettingsCategory: String, CaseIterable, Identifiable {
case destination
case general
case speed
case schedule
case advanced
case scripts
var id: String { rawValue }
}
struct SettingsView: View {
@Binding private var selectedCategory: SettingsCategory
@State private var selection: SettingsCategory?
init(selectedCategory: Binding<SettingsCategory>) {
_selectedCategory = Binding<SettingsCategory>(
get: { selectedCategory.wrappedValue },
set: { newCategory in
selectedCategory.wrappedValue = newCategory
}
)
}
var body: some View {
List(SettingsCategory.allCases, selection: $selection) { category in
Text(category.rawValue.capitalized)
.tag(category)
}
.onChange(of: selection) { [oldCategory = selection] newCategory in
if let newCategory = newCategory {
selection = newCategory
selectedCategory = newCategory
} else {
selection = oldCategory
}
}
}
}
CodePudding user response:
you could try adding .onChange
to the List
, such as:
.onChange(of: selectedCategory) { val in
if val == nil {
selectedCategory = .general // <-- make sure never nil
}
}