簡體   English   中英

保存 NavigationLink 選擇

[英]Save NavigationLink selection

我正在構建一個帶有側邊欄的桌面應用程序。 它的行為應該是這樣的:

  1. 用戶可以在邊欄中顯示的列表中添加和刪除元素。
  2. 重新啟動應用程序時,選定的列表元素需要保持選中狀態。
  3. 添加新元素后,新元素將被選中。

我在實現 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)")
    }
}

這有兩個問題:

  1. 我想將新元素設置為選定元素,因此我需要將self.$selectedElementId更新為element.objectId 我怎樣才能做到這一點? wrappedValue似乎是只讀的。
  2. 對於當前選定的屬性,我需要將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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM