swiftswiftuitablecolumn

Discrepancy between Preview and Running App


I am working on an app running on a Mac using XCode 16.2..

One view is displaying a table with some person data. ContentViewPreview (Person) 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. PersonEditorPreview In preview mode everything renders as expected, but when running the app, the edit page does not display the table with the address data.
incomplete PersonEditor when running the app

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?


Solution

  • Finally 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.