I am working on an app running on a Mac using XCode 16.2..
One view is displaying a table with some person data.
The person might have several addresses. Because
SwiftUI can only display 10 columns as a maximum, (several) address data is displayed in one table column and row.
The table has a context menu, which will allow editing of the person data. On this editing page, the address list should
be rendered as a table.
In preview mode everything renders as expected, but when running the app, the edit page does not display the
table with the address data.
ContentView is displaying the Person Table. If one Person is selected, the Edit-Context-Menu is enabled. This should display the PersonTableEditor. Unfortunately this view is not complete. What am I doing wrong?
struct ContentView: View {
@State private var showingEditPage = false
@State private var selected: Person.ID?
@State var persons: [Person]
@State var sortOrder = [KeyPathComparator(\Person.id)]
var body: some View {
VStack {
Table(persons, selection: $selected, sortOrder: $sortOrder) {
TableColumn("Name", value: \.name).width(100.0)
TableColumn("Fistname", value: \.firstName ).width(100.0)
TableColumn("Birthday", value: \.birthDate) { pers in
Text(formatter.string(from: pers.birthDate))
}.width(100.0)
TableColumn("Addresses", value: \.addresses, comparator: KeyPathComparator(\[Address].description)) { pers in
ForEach(pers.addresses) { adr in
HStack {
Text(adr.street)
Text(adr.zip)
Text(adr.city)
}}
}
}
.contextMenu(forSelectionType: Person.ID.self) { _ in
Button("Edit ...") { showingEditPage = true}.disabled(selected == nil)
}
.sheet(isPresented: $showingEditPage) {
PersonTableEditor(person: selectedPerson()!).id(UUID())
}
}
.padding()
}
private func selectedPerson() -> Person? {
guard let person = persons.first(where: {return $0.id == selected}) else { return nil
}
return person
}
private let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}()
}
struct PersonTableEditor: View {
@State var showingEditPage = false
@State private var selected: Address.ID?
@State var person: Person
@State var sortOrder = [KeyPathComparator(\Address.id)]
@State var perferred = false
var body: some View {
VStack {
TextField("Name", text: $person.name)
TextField("First", text: $person.firstName)
DatePicker("Birthday", selection: $person.birthDate, displayedComponents: [.date])
Table(person.addresses, selection: $selected, sortOrder: $sortOrder) {
TableColumn("Preferred", value: \Address.preferred, comparator: BoolComparator()) { address in
Text(address.preferred ? "Yes" : "No")
}
TableColumn("Street", value: \.street)
TableColumn("Zip", value: \.zip)
TableColumn("City", value: \.city)
}.id(UUID())
.contextMenu(forSelectionType: Person.ID.self) { _ in
Button("Edit ...") { showingEditPage = true}.disabled(selected == nil)
}
.sheet(isPresented: $showingEditPage) {
AddressTableEditor(address: selectedAddress()!)
}
}
.padding()
}
private func selectedAddress() -> Address? {
guard let adrs = person.addresses.first(where: {return $0.id == selected}) else { return nil
}
return adrs
}
}
let persons = [
Person(name: "Meier", firstName: "Hans", birthDate: Date(), addresses: [
Address(street: "Mauernstr. 1", zip: "10000", city: "Berlin", preferred: false),
Address(street: "Hafenstr. 2", zip: "20000", city: "Hamburg", preferred: true)
])
]
I have tried several setups for the PersonTableEditor without any success. Is the an other way to provide an editing architecture?
I am working on an app running on a Mac using XCode 16.2..
One view is displaying a table with some person data.
The person might have several addresses. Because
SwiftUI can only display 10 columns as a maximum, (several) address data is displayed in one table column and row.
The table has a context menu, which will allow editing of the person data. On this editing page, the address list should
be rendered as a table.
In preview mode everything renders as expected, but when running the app, the edit page does not display the
table with the address data.
ContentView is displaying the Person Table. If one Person is selected, the Edit-Context-Menu is enabled. This should display the PersonTableEditor. Unfortunately this view is not complete. What am I doing wrong?
struct ContentView: View {
@State private var showingEditPage = false
@State private var selected: Person.ID?
@State var persons: [Person]
@State var sortOrder = [KeyPathComparator(\Person.id)]
var body: some View {
VStack {
Table(persons, selection: $selected, sortOrder: $sortOrder) {
TableColumn("Name", value: \.name).width(100.0)
TableColumn("Fistname", value: \.firstName ).width(100.0)
TableColumn("Birthday", value: \.birthDate) { pers in
Text(formatter.string(from: pers.birthDate))
}.width(100.0)
TableColumn("Addresses", value: \.addresses, comparator: KeyPathComparator(\[Address].description)) { pers in
ForEach(pers.addresses) { adr in
HStack {
Text(adr.street)
Text(adr.zip)
Text(adr.city)
}}
}
}
.contextMenu(forSelectionType: Person.ID.self) { _ in
Button("Edit ...") { showingEditPage = true}.disabled(selected == nil)
}
.sheet(isPresented: $showingEditPage) {
PersonTableEditor(person: selectedPerson()!).id(UUID())
}
}
.padding()
}
private func selectedPerson() -> Person? {
guard let person = persons.first(where: {return $0.id == selected}) else { return nil
}
return person
}
private let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}()
}
struct PersonTableEditor: View {
@State var showingEditPage = false
@State private var selected: Address.ID?
@State var person: Person
@State var sortOrder = [KeyPathComparator(\Address.id)]
@State var perferred = false
var body: some View {
VStack {
TextField("Name", text: $person.name)
TextField("First", text: $person.firstName)
DatePicker("Birthday", selection: $person.birthDate, displayedComponents: [.date])
Table(person.addresses, selection: $selected, sortOrder: $sortOrder) {
TableColumn("Preferred", value: \Address.preferred, comparator: BoolComparator()) { address in
Text(address.preferred ? "Yes" : "No")
}
TableColumn("Street", value: \.street)
TableColumn("Zip", value: \.zip)
TableColumn("City", value: \.city)
}.id(UUID())
.contextMenu(forSelectionType: Person.ID.self) { _ in
Button("Edit ...") { showingEditPage = true}.disabled(selected == nil)
}
.sheet(isPresented: $showingEditPage) {
AddressTableEditor(address: selectedAddress()!)
}
}
.padding()
}
private func selectedAddress() -> Address? {
guard let adrs = person.addresses.first(where: {return $0.id == selected}) else { return nil
}
return adrs
}
}
let persons = [
Person(name: "Meier", firstName: "Hans", birthDate: Date(), addresses: [
Address(street: "Mauernstr. 1", zip: "10000", city: "Berlin", preferred: false),
Address(street: "Hafenstr. 2", zip: "20000", city: "Hamburg", preferred: true)
])
]
I have tried several setups for the PersonTableEditor without any success. Is the an other way to provide an editing architecture?
Share Improve this question edited yesterday koen 5,7297 gold badges58 silver badges101 bronze badges asked 2 days ago hjbflyerhjbflyer 7911 bronze badges 6- Please show a minimal reproducible example - something I can copy and paste into a new Xcode project and reproduce the issue. – Sweeper Commented 2 days ago
- Your question should be self-contained. Please make an effort to isolate the problem and post only the minimal code required to reproduce the problem. – Sweeper Commented 2 days ago
- This is the minimal example code. It shows the working Previews and the bug when running the App. – hjbflyer Commented 2 days ago
- In my example there is a Table with context menus. The context menu is opening an other View (.sheet). This view is not able to display an other Table. I think it is a BUG in the Table implementation. There is no log entry. In the view there is nothing (or just an EmptyView instead of the Table). In the preview, everything renders correctly. – hjbflyer Commented 7 hours ago
- @Sweeper. I have prepared a smaller example to demonstrate the bug. – hjbflyer Commented 6 hours ago
1 Answer
Reset to default 0Finally I have found a work around. Just append the .frame(height: 200.0)
modifier to the VStack with a value large enough to display the table. If the value is too small, the table won't be rendered.