[英]Save NavigationLink selection
我正在構建一個帶有側邊欄的桌面應用程序。 它的行為應該是這樣的:
我在實現 2. 和 3. 點時遇到問題。
這是我的看法:
import SwiftUI
import CoreData
struct Sidebar: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Element.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Element.title, ascending: false)])
private var elements: FetchedResults<Element>
@State var selectedElementId: NSManagedObjectID?
var body: some View {
VStack (alignment: .leading) {
List(self.elements) { element in
NavigationLink(destination: ContentView(), tag: element.objectID, selection: self.$selectedElementId) {
Text(element.title!)
}
}
.listStyle(SidebarListStyle())
HStack {
Button(action: addElement) {
Image(systemName: "plus")
}
Button(action: removeElement) {
Image(systemName: "minus")
}
}
}
}
private func addElement() {
let element = Element(context: viewContext)
element.title = "My title"
do {
try viewContext.save()
} catch {
print("Unresolved error \(error)")
}
}
private func removeElement() {
if self.$selectedElementId.wrappedValue == nil {
return
}
let element = viewContext.object(with: self.$selectedElementId.wrappedValue!)
viewContext.delete(element)
do {
try viewContext.save()
} catch {
print("Unresolved error \(error)")
}
}
}
為了實現 2. 和 3. 點,我嘗試了這個:
我使用核心數據作為我的存儲。 我的想法是有一個屬性Element.selection
可以告訴我最后選擇了哪個列表項,但我不知道如何更新$selectedElementId
。 我在addElement
中嘗試了這個,但它沒有按預期工作:
private func addElement() {
let element = Element(context: viewContext)
element.title = "My title"
if self.$selectedElementId.wrappedValue != nil {
let selectedElement = viewContext.object(with: self.$selectedElementId.wrappedValue!)
selectedElement.entity.selected = false
}
self.$selectedElementId.wrappedValue = element.objectID
do {
try viewContext.save()
} catch {
print("Unresolved error \(error)")
}
}
這有兩個問題:
self.$selectedElementId
更新為element.objectId
。 我怎樣才能做到這一點? wrappedValue
似乎是只讀的。Element.selection
設置為false
。 我已經在selectedElement
中獲取了元素,但我不知道如何更新任何值,因為它的類型NSManagedObject
,而不是Element
。我是 Swift 的新手。 請讓我知道我的方法中是否還有其他錯誤或可以做得更好的元素。
您需要解決一些問題才能使其正常工作。
首先,您的addElement()
方法不起作用,因為您需要直接設置 state var 以觸發更新,即:
// Do this
self.selectedElementId = element.objectID
// Not this
self.$selectedElementId.wrappedValue = element.objectID
其次,您嘗試將應用程序 state (用戶的選擇)存儲在您的數據庫中。 通常,數據庫用於用戶數據,應用程序 state 應存儲在其他位置。 在 Mac 和 iOS 上,該位置通常是UserDefaults
(在@AppStorage
中也稱為 @AppStorage),但對於您想要的 SwiftUI 有一個特定的解決方案: @SceneStorage
。 您放入SceneStorage
的任何內容都會按場景保存,這非常適合您的情況,因為用戶可以打開多個 windows 並在每個 window 中進行不同的選擇。
更好的是,SceneStorage 非常簡單,只需將屬性@SceneStorage
與唯一鍵添加到應保存的任何屬性中:
@SceneStorage("com.appname.selected_element") var selectedElementId: NSManagedObjectID?
......哦,除了不會編譯,因為SceneStorage
只適用於一些基本類型,如 Int 和 String。 如果將字符串屬性identifier
添加到Element
,則可以在任何地方使用它而不是 NSManagedObjectID:
struct Sidebar: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Element.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Element.title, ascending: false)])
private var elements: FetchedResults<Element>
@SceneStorage("com.appname.selected_element") var selectedElementId: String?
var body: some View {
VStack (alignment: .leading) {
List(self.elements) { element in
NavigationLink(destination: ContentView(),
tag: element.identifier!,
selection: self.$selectedElementId) {
Text(element.title!)
}
}
.listStyle(SidebarListStyle())
HStack {
Button(action: addElement) {
Image(systemName: "plus")
}
Button(action: removeElement) {
Image(systemName: "minus")
}
}
}
}
private func addElement() {
let element = Element(context: viewContext)
element.title = "My title"
// set identifier to a random UUID
element.identifier = UUID().uuidString
do {
try viewContext.save()
} catch {
print("Unresolved error \(error)")
}
// select our newly-added element
self.selectedElementId = element.identifier
}
private func removeElement() {
// TODO: fetch selected object with identifier == $selectedElementId
}
}
可能有幫助的另一件事是在您的List
中設置選擇,但這似乎不是必需的:
List(self.elements, id: \.identifier, selection:$selectedElementId) {
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.