Swiftui 中的默认导航 link

Default navigation link in Swiftui

我正在开发一个应用程序,该应用程序使用带有详细信息视图的传统侧边栏导航。我综合了这个应用程序来说明两个问题。

  1. 当应用程序启动时,详细视图是空的。如何以编程方式 select 侧边栏中的条目显示在详细信息视图中?

  2. 侧边栏允许滑动删除。如果 selected 行(显示在详细视图中的行)被删除,它仍然显示在详细视图中。例如,如何使用空视图更新详细视图?

以下是说明问题的应用源代码:

import SwiftUI

class Model: ObservableObject {
  var items = [Item("")]
  
  static var loadData: Model {
    let model = Model()
    model.items = [Item("Books"), Item("Videos"), Item("Pics"), Item("Cars")]
    
    return model
  }
}

class Item: Identifiable, Hashable {
  static func == (lhs: Item, rhs: Item) -> Bool {
    lhs.name == rhs.name
  }
  
  func hash(into hasher: inout Hasher) {
    hasher.combine(id)
  }

  let id = UUID()
  
  @Published var name: String
  
  init(_ name: String) {
    self.name = name
  }
}


@main
struct IBTSimulatorApp: App {
  @StateObject var model = Model.loadData
    
  var body: some Scene {
    WindowGroup {
      ContentView()
        .environmentObject(model)
    }
  }
}

struct ContentView: View {
  @EnvironmentObject var model: Model
  
  var body: some View {
    NavigationView {
      List {
        ForEach($model.items, id: \.self) { $item in
          NavigationLink(item.name, destination: Text(item.name))
        }
        .onDelete(perform: deleteItems)
      }
      .toolbar {
        ToolbarItem(placement: .navigationBarTrailing)  {
          Button(action: addItem) {
            Label("Add Item", systemImage: "plus")
          }
        }
      }
    }
  }
  
  private func addItem() {
    withAnimation {
      model.items.append(Item("New item (\(model.items.count))"))
      model.objectWillChange.send()
    }
  }

  private func deleteItems(offsets: IndexSet) {
    withAnimation {
      model.items.remove(atOffsets: offsets)
      model.objectWillChange.send()
    }
  }
}

对于 1. 您可以使用带有标签和选择的 NavigationLink 版本,并将活动选择保存在持久的 AppStoragevar.

对于 2。我希望您可以将选择设置为零,但由于某些原因这不起作用。但您可以将其设置为侧边栏列表中的第一项。

作为一般说明,您应该将 Item 设为结构而不是 class。只有发布的模型应该是 class.

class Model: ObservableObject {
    var items: [Item] = []
    
    static var loadData: Model {
        let model = Model()
        model.items = [Item("Books"), Item("Videos"), Item("Pics"), Item("Cars")]
        
        return model
    }
}

struct Item: Identifiable { // Change from class to struct!
    
    let id = UUID()
    var name: String
    
    init(_ name: String) {
        self.name = name
    }
}


struct ContentView: View {
    @StateObject var model = Model.loadData
    
    @AppStorage("selectemItem") var selected: String? // bind to persisted var here
    
    
    var body: some View {
        NavigationView {
            List {
                ForEach(model.items) { item in //no .id needed as Item is identifiable
                    NavigationLink(tag: item.id.uuidString, selection: $selected) { // use link with selection here
                        Text(item.name)
                    } label: {
                        Text(item.name)
                    }
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing)  {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
            
            Text("Nothing selected")
        }
    }
    
    private func addItem() {
        withAnimation {
            model.objectWillChange.send()
            model.items.append(Item("New item (\(model.items.count))"))
        }
    }
    
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
//            model.objectWillChange.send() // not necessary if Item is struct
            self.selected = nil // for some reaseon this does not work
            self.selected = model.items.first?.id.uuidString // selects first item
            model.items.remove(atOffsets: offsets)
        }
    }
}