[英]Save NavigationLink selection
I'm building a desktop application that has a sidebar.我正在构建一个带有侧边栏的桌面应用程序。 It should behave like this:
它的行为应该是这样的:
I'm having issues with achieving the 2. and 3. point.我在实现 2. 和 3. 点时遇到问题。
This is my view:这是我的看法:
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)")
}
}
}
To achieve the 2. and 3. point, I tried this:为了实现 2. 和 3. 点,我尝试了这个:
I'm using core data as my storage.我使用核心数据作为我的存储。 My idea is to have an attribute
Element.selection
which would tell me what list item was last selected, but I don't know how to update the $selectedElementId
.我的想法是有一个属性
Element.selection
可以告诉我最后选择了哪个列表项,但我不知道如何更新$selectedElementId
。 I tried this in addElement
but it doesn't work as expected:我在
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)")
}
}
This has two issues:这有两个问题:
self.$selectedElementId
to element.objectId
.self.$selectedElementId
更新为element.objectId
。 How can I do that?wrappedValue
seems to be read-only. wrappedValue
似乎是只读的。Element.selection
to false
for the currently selected attribute.Element.selection
设置为false
。 I've fetched the element in selectedElement
, but I don't know how to update any values as it's of type NSManagedObject
, not Element
.selectedElement
中获取了元素,但我不知道如何更新任何值,因为它的类型NSManagedObject
,而不是Element
。 I'm new to Swift.我是 Swift 的新手。 Please let me know if there are also other elements of my approach that are wrong or could be done better.
请让我知道我的方法中是否还有其他错误或可以做得更好的元素。
You'll need to fix a few things to get this working.您需要解决一些问题才能使其正常工作。
First, your addElement()
method isn't working because you need to set your state var directly to trigger an update, ie:首先,您的
addElement()
方法不起作用,因为您需要直接设置 state var 以触发更新,即:
// Do this
self.selectedElementId = element.objectID
// Not this
self.$selectedElementId.wrappedValue = element.objectID
Second, you're trying to store application state — the user's selection — in your database.其次,您尝试将应用程序 state (用户的选择)存储在您的数据库中。 In general, databases are for user data and app state should be stored elsewhere.
通常,数据库用于用户数据,应用程序 state 应存储在其他位置。 On Mac and iOS that place is usually
UserDefaults
(aka @AppStorage
in SwiftUI), but for what you want SwiftUI has a specific solution: @SceneStorage
.在 Mac 和 iOS 上,该位置通常是
UserDefaults
(在@AppStorage
中也称为 @AppStorage),但对于您想要的 SwiftUI 有一个特定的解决方案: @SceneStorage
。 Anything you put in SceneStorage
is saved per- scene , which is perfect for your case because users can open multiple windows and have different selection in each window.您放入
SceneStorage
的任何内容都会按场景保存,这非常适合您的情况,因为用户可以打开多个 windows 并在每个 window 中进行不同的选择。
And better yet, SceneStorage is super simple, just add the property @SceneStorage
with a unique key to whatever property should be saved:更好的是,SceneStorage 非常简单,只需将属性
@SceneStorage
与唯一键添加到应保存的任何属性中:
@SceneStorage("com.appname.selected_element") var selectedElementId: NSManagedObjectID?
… oh except that won't compile because SceneStorage
only works for a few basic types like Int and String. ......哦,除了不会编译,因为
SceneStorage
只适用于一些基本类型,如 Int 和 String。 If you add a string property identifier
to Element
, then you can use it everywhere instead of NSManagedObjectID:如果将字符串属性
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
}
}
One more thing that may help is setting the selection in your List
, but this doesn't seem to be required:可能有帮助的另一件事是在您的
List
中设置选择,但这似乎不是必需的:
List(self.elements, id: \.identifier, selection:$selectedElementId) {
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.