*** NOTE: This question concerns macOS, not iOS ***
Context:
I have a list of items that serves as a "Master" view in a standard Master-Detail arrangement. You select an item from the list, and the detail view updates:
In SwiftUI, this is powered by a List
like this:
struct RuleListView: View
{
@State var rules: [Rule]
@State var selectedRuleUUID: UUID?
var body: some View
{
List(rules, id: \.uuid, selection: $selectedRuleUUID) { rule in
RuleListRow(rule: rule, isSelected: (selectedRuleUUID == rule.uuid))
}
}
}
The Problem:
The name of each item in the list is user-editable. In AppKit, when using NSTableView
or NSOutlineView
for the list, the first click on a row selects that row and the second click would then begin editing the NSTextField
that contains the name.
In SwiftUI, things have apparently gotten dumber. Clicking on ANY text in the list immediately begins editing that text, which makes selecting a row VERY difficult—you have to click on the 2 pixels above or below the TextField
in each row.
To combat this, I've stashed a clear Button()
on top of every row that's not selected. This button intercepts the click before it reaches the TextField()
and selects the row. When the row is selected, the Button()
is no longer included and subsequent clicks then select the TextField()
:
struct RuleListRow: View
{
var rule: Rule
var isSelected: Bool
var body: some View
{
ZStack
{
Label {
TextField("", text: $rule.name)
.labelsHidden()
} icon: {
Image(systemName: "lasso.sparkles")
}
if !isSelected
{
Button {
// No-op
} label: {
EmptyView()
}
.buttonStyle(.plain)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.clear)
.contentShape(Rectangle())
}
}
}
}
Question:
The above works, but...this can't be correct, right? I've missed something basic that makes List
behave properly when it contains TextField
rows, right? There's some magic viewModifier I'm supposed to stick somewhere, I'm sure.
What is the correct, canonical way to solve this issue using Swift 5.5 and targeting macOS 11.0+?
Note:
My first approach was to simply disable the TextField
when the row isn't selected, but that caused the text to appear "dimmed" and I couldn't find a way to override the text color when the TextField
is disabled.