I have a SwiftUI Table
on macOS. (I'm NOT using a List
.) I want it to behave the same as a Cocoa table: when a user clicks on a row, focus should shift to the table AND the clicked row should be selected. Right now, the table is gaining focus after one click, but the selection isn't being set. It takes another click for that to happen. Quite annoying.
Table(tableModel.roads, selection: $tableModel.selectedRoadID) {
TableColumn("ED") { road in
Text(road.district)
}
.width(35)
TableColumn("Road") { road in
Text(road.name)
}
}
.frame(height: nil) // Adjust height as needed
I tried adding .focused
and an .onTapGesture
setting the focus state, but the behavior didn't change.
@FocusState private var roadsTableFocused: Bool
// ...
.focused($roadsTableFocused)
.onTapGesture {
roadsTableFocused = true
}
Thanks in advance for your help!
Update: Thanks to Sweeper for the question. This has something to do with the @Observable
model class I'm using. When I use a @State
variable in the same View
class for the TextField
, it works fine. Same when I switch to the table from any non-TextField
widgets, even though they're also bound to state variables in the model class. Here's an MRE of the issue in the form of a macOS SwiftUI Playground application:
ContentView.swift
import AppKit
import SwiftUI
struct Road: Identifiable {
let id: Int
let name: String
let district: String
}
@Observable class TableModel {
var observableFieldValue: String = ""
}
struct ContentView: View {
@State private var tableModel: TableModel
@State private var selectedRoadID: Road.ID?
@State private var localFieldValue: String = ""
@State private var roads: [Road] = [
Road(id: 1, name: "Market", district: "5"),
Road(id: 2, name: "Howard", district: "5"),
Road(id: 3, name: "Gough", district: "5"),
Road(id: 4, name: "Click Here", district: "5"),
]
init() {
let viewModel = TableModel()
_tableModel = State(wrappedValue: viewModel)
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
TextField("Observable binding", text: $tableModel.observableFieldValue)
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("ContentView binding", text: $localFieldValue)
.textFieldStyle(RoundedBorderTextFieldStyle())
Table(roads, selection: $selectedRoadID) {
TableColumn("ED") { road in
Text(road.district)
}
.width(35)
TableColumn("Road") { road in
Text(road.name)
}
}
}
.frame(width: 300, height: 220, alignment: .leading)
}
}
#Preview {
ContentView()
}
Update 2: I've made a video of what I'm seeing. I'm on macOS 15.2 (24C101) using Xcode 16.2 (16C5032a). I've built a full test application using the above code, slightly modified from before to remove the Playground.
Is it really the case that no one else is seeing this behavior?