hjbflyer
hjbflyer

Reputation: 79

SwiftUI table view not updated on selection change

I have implemented a tree view representing different types of objects. Details for these objects should be displayed in a tabbed table view. If I do select an object in the tree, the details of this object will be displayed in the table. This works as expected, as long as i do select objects on different hierarchy levels. But if I select objects on the same hierarchy level the table view is not updated.

I assume I do make a mistake in my table views so they are not always updates on selection change. screenshot

import SwiftUI

struct ContentView: View {

  @State var navSelection = Set<Tree<String>>()

  var body: some View {
    NavigationSplitView {
      List(treeNodes, id: \.value, children: \.children, selection: $navSelection) { tree in
        NavigationLink(value: tree) {
          TreeView(node: tree)
        }
      }
      .contextMenu(forSelectionType: Tree<String>.self) { nodes in
        if nodes.first!.value == "House 1" {
          Button("House 1 seleced") {}
        } else {
          Button("No node or more than 1 selected") {}
        }
      }
      .listStyle(SidebarListStyle())
    } detail: {
      TabView {
        if let tab = navSelection.first {
          if tab.objectRef is Root {
            Tab("Houses", image: "Haus") {
              // swiftlint: disable force_cast
              HousesView(data: (tab.objectRef as! Root).houses)
            }
          } else if tab.objectRef is House {
            Tab("Apartments", image: "Wohnung") {
              ApartmentView(data: (tab.objectRef as! House).apartments)
              // swiftlint: enable force_cast
            }
          }
        }
        Tab("Info", systemImage: "pencil") {
          Text("navSelection: \(navSelection.first?.value)")
        }
      }
    }
  }
}
struct ApartmentView: View {

  @State var data: [Apartment]
    var body: some View {
      Table(data) {
        TableColumn("Floor", value: \.floor)
        TableColumn("Rooms", value: \.rooms)
      }
    }
}
struct HousesView: View {
  @State var data: [House]
  var body: some View {
    Table(data) {
      TableColumn("Name", value: \.name )
      TableColumn("Addresse", value: \.address)
    }
  }
}
class Tree<String: Hashable>: Hashable, ObservableObject {

  var value: String
  var icon: String?
  var objectRef: AnyObject
  var children: [Tree]?

  init(value: String, icon: String? = nil, objRef: AnyObject, children: [Tree]? = nil) {
    self.value = value
    self.icon = icon
    self.objectRef = objRef
    self.children = children
  }

  static func == (lhs: Tree<String>, rhs: Tree<String>) -> Bool {
    return lhs.value == rhs.value
  }

  func hash(into hasher: inout Hasher) {
    hasher.combine(value)
  }
}

// MARK: - init demo data
class Root {
  var name: String
  var someData: String
  var houses: [House]
  init(name: String, someData: String, houses: [House] = []) {
    self.name = name
    self.someData = someData
    self.houses = houses
  }
}

class House: Identifiable {
  var name: String
  var address: String
  var apartments: [Apartment]
  init(name: String, address: String, apartments: [Apartment] = []) {
    self.name = name
    self.address = address
    self.apartments = apartments
  }
}

class Apartment: Identifiable {
  var id = UUID()
  var floor: String
  var rooms: String
  init(floor: String, rooms: String) {
    self.floor = floor
    self.rooms = rooms
  }
}

var root = Root(name: "Root", someData: "some test data", houses: houses)

var houses = [House(name: "House 1", address: "street address of house 1", apartments: apartments1),
              House(name: "House 2", address: "street address of house 2"),
              House(name: "House 3", address: "street address of house 3", apartments: apartments2)]

var apartments1 = [Apartment(floor: "1. left", rooms: "3"), Apartment(floor: "1. right", rooms: "1")]
var apartments2 = [Apartment(floor: "2. left", rooms: "2"), Apartment(floor: "2. right", rooms: "2")]

/// example data using string values
let treeNodes: [Tree<String>] = [
  .init(
    value: "Root", icon: "Block",
    objRef: root, children: [
      .init(value: "House 1", icon: "Haus", objRef: houses[0],
            children: [
              .init(value: "Apartments1 1", icon: "Wohnung", objRef: apartments1[0]),
              .init(value: "Apartments1 2", icon: "Wohnung", objRef: apartments1[1])
            ]
           ),
      .init(value: "House 2", icon: "Haus", objRef: houses[1]),
      .init(value: "House 3", icon: "Haus", objRef: houses[2],
        children: [
          .init(value: "Apartments2 1", icon: "Wohnung", objRef: apartments2[0]),
          .init(value: "Apartments2 2", icon: "Wohnung", objRef: apartments2[1])
        ]
      )
    ]
  )
]

Hopefully someone can help me change my example in a way that every selection change will update the tables.

Upvotes: 0

Views: 38

Answers (0)

Related Questions