Core data SwiftUI/Core数据-更新详细视图时在列表视图和详细视图之间进行非自愿导航

Core data SwiftUI/Core数据-更新详细视图时在列表视图和详细视图之间进行非自愿导航,core-data,swiftui-list,swiftui-navigationlink,swiftui,Core Data,Swiftui List,Swiftui Navigationlink,Swiftui,我正在使用TabView/NavigationView&NavigationLink以编程方式从列表视图导航到详细视图,当我将布尔属性“pinted”更新为true并在详细视图中保存核心数据实体时,我得到了以下不幸的副作用: 非自愿导航返回列表视图,然后再次返回详细视图或 非自愿导航到同一详细视图的另一个副本,然后返回列表视图 我准备了一个小的 在列表视图中,我使用@FetchRequest查询列表并对以下内容进行排序: @FetchRequest(entity: Task.entity

我正在使用TabView/NavigationView&NavigationLink以编程方式从列表视图导航到详细视图,当我将布尔属性“pinted”更新为true并在详细视图中保存核心数据实体时,我得到了以下不幸的副作用:

  • 非自愿导航返回列表视图,然后再次返回详细视图
  • 非自愿导航到同一详细视图的另一个副本,然后返回列表视图
  • 我准备了一个小的

    在列表视图中,我使用@FetchRequest查询列表并对以下内容进行排序:

        @FetchRequest(entity: Task.entity(),sortDescriptors: [NSSortDescriptor(key: "pinned", ascending: false),
                                                              NSSortDescriptor(key: "created", ascending: true),
                                                              NSSortDescriptor(key: "name", ascending: true)])
    
    
    List() {
       ForEach(tasks, id: \.self) { task in
           NavigationLink(destination: DetailsView(task: task), tag: task.id!.uuidString, selection: self.$selectionId) {
             HStack() {
                ...
             }
          }
    }
    
    在列表视图中,我使用以下内容:

        @FetchRequest(entity: Task.entity(),sortDescriptors: [NSSortDescriptor(key: "pinned", ascending: false),
                                                              NSSortDescriptor(key: "created", ascending: true),
                                                              NSSortDescriptor(key: "name", ascending: true)])
    
    
    List() {
       ForEach(tasks, id: \.self) { task in
           NavigationLink(destination: DetailsView(task: task), tag: task.id!.uuidString, selection: self.$selectionId) {
             HStack() {
                ...
             }
          }
    }
    

    (1) 如果我省略了“NSSortDescriptor(key:“pinted”…”),我就看不到这种行为

    (2) 如果在NavigationLink()中省略“tag:”和“selection:”参数,则看不到行为。但我需要能够在创建新任务实体时以编程方式触发导航链接

    (3) 当列表中只有一个实体或更改列表中第一个实体的“pinted”布尔属性值时,似乎永远不会发生这种情况

    (4) 我得到警告:

    [TableView]仅警告一次:UITableView被告知布局其可见单元格和其他内容,而不在视图层次结构中(尚未将表视图或其超级视图之一添加到窗口中)

    列表视图(TasksListView)的父视图包含一个选项卡视图:

    struct ContentView: View {
        var body: some View {
            TabView {
                NavigationView {
                    TasksListView()
                }
                .tabItem {
                    Image(systemName: "tray.full")
                        .font(.title)
                    Text("Master")
                }
                NavigationView {
                    EmptyView()
                }
                .tabItem {
                    Image(systemName: "magnifyingglass")
                        .font(.title)
                    Text("Search")
                }
            }  
        }
    }
    
    struct TasksListView: View {
        
        // NSManagedObjectContext
        @Environment(\.managedObjectContext) var viewContext
    
        // Results of fetch request for tasks:
        @FetchRequest(entity: Task.entity(),sortDescriptors: [NSSortDescriptor(key: "pinned", ascending: false),
                                                              NSSortDescriptor(key: "created", ascending: true),
                                                              NSSortDescriptor(key: "name", ascending: true)])
        var tasks: FetchedResults<Task>
        
        // when we create a new task and navigate to it programitically
        @State var selectionId : String?
        @State var newTask : Task?
    
    
        var body: some View {
            
            List() {
                ForEach(tasks, id: \.self) { task in
                    NavigationLink(destination: DetailsView(task: task), tag: task.id!.uuidString, selection: self.$selectionId) {
                        HStack() {
                            VStack(alignment: .leading) {
                                Text("\(task.name ?? "unknown")")
                                    .font(Font.headline.weight(.light))
                                    .padding(.bottom,5)
                                Text("Created:\t\(task.created ?? Date(), formatter: Self.dateFormatter)")
                                    .font(Font.subheadline.weight(.light))
                                    .padding(.bottom,5)
                                if task.due != nil {
                                    Text("Due:\t\t\(task.due!, formatter: Self.dateFormatter)")
                                        .font(Font.subheadline.weight(.light))
                                        .padding(.bottom,5)
                                }
                            }
                        }
                    }
                    
                }
            }
            .navigationBarTitle(Text("Tasks"),displayMode: .inline)
            .navigationBarItems(trailing: rightButton)
        }
        
        
        var rightButton: some View {
            Image(systemName: "plus.circle")
                .foregroundColor(Color(UIColor.systemBlue))
                .font(.title)
                .contentShape(Rectangle())
                .onTapGesture {
                    // create a new task and navigate to it's detailed view to add values
                    Task.create(in: self.viewContext) { (task, success, error) in
                        if success {
                            self.newTask = task
                            self.selectionId = task!.id!.uuidString
                        }
                    }
                    
            }
        }
    }
    
    struct DetailsView: View {
        
        // NSManagedObjectContext
        @Environment(\.managedObjectContext) var viewContext
            
        @ObservedObject var task : Task
        
        @State var name : String = ""
        @State var dueDate : Date = Date()
        @State var hasDueDate : Bool = false
        @State var isPinned : Bool = false
        
        var body: some View {
            
            List() {
                
                Section() {
                    Toggle(isOn: self.$isPinned) {
                        Text("Pinned")
                    }
                }
                
                Section() {
                    TextField("Name", text: self.$name)
                        .font(Font.headline.weight(.light))
                    Text("\(task.id?.uuidString ?? "unknown")")
                        .font(Font.headline.weight(.light))
                }
                
                Section() {
                    HStack() {
                        Text("Created")
                        Spacer()
                        Text("\(task.created ?? Date(), formatter: Self.dateFormatter)")
                            .font(Font.subheadline.weight(.light))
                    }
                
                    Toggle(isOn: self.$hasDueDate) {
                        Text("Set Due Date")
                    }
                    
                    if self.hasDueDate {
                        DatePicker("Due Date", selection: self.$dueDate, in: Date()... , displayedComponents: [.hourAndMinute, .date])
                    }
                }
            }
            .navigationBarTitle(Text("Task Details"),displayMode: .inline)
            .navigationBarItems(trailing: rightButton)
            .listStyle(GroupedListStyle())
            .onAppear() {
                if self.task.pinned {
                    self.isPinned = true
                }
                if self.task.name != nil {
                    self.name = self.task.name!
                }
                if self.task.due != nil {
                    self.dueDate = self.task.due!
                    self.hasDueDate = true
                }
            }
            
        }
        
        // save button
        
        var rightButton: some View {
            
            Button("Save") {
    
                // save values in task & save:
                self.task.pinned = self.isPinned
                if self.hasDueDate  {
                    self.task.due = self.dueDate
                }
                
                if self.name.count > 0 {
                    self.task.name = self.name
                }
                
                Task.save(in: self.viewContext) { (success, error) in
                    DispatchQueue.main.async {
                        if success {
                            print("Task saved")
                        }
                        else {
                            print("****** Error: Task can't be saved, error = \(error!.localizedDescription)")
                        }
                    }
                }
            }
            .contentShape(Rectangle())        
        }
        
        
    }
    
    extension Task {
        
        static func save(in managedObjectContext: NSManagedObjectContext, completion: @escaping (Bool, NSError?) -> Void ) {
            managedObjectContext.performAndWait() {
                do {
                    try managedObjectContext.save()
                    completion(true, nil)
                } catch {
                    let nserror = error as NSError
                    print("****** Error: Unresolved error \(nserror), \(nserror.userInfo)")
                    completion(false, nserror)
                }
            }
         }
    }
    
    
    
    
    
    struct ContentView:View{
    var body:一些观点{
    TabView{
    导航视图{
    TasksListView()
    }
    .tabItem{
    图像(系统名称:“托盘已满”)
    .font(.title)
    文本(“主控”)
    }
    导航视图{
    EmptyView()
    }
    .tabItem{
    图像(系统名称:“放大镜”)
    .font(.title)
    文本(“搜索”)
    }
    }  
    }
    }
    结构任务列表视图:视图{
    //NSManagedObjectContext
    @环境(\.managedObjectContext)变量viewContext
    //任务的获取请求的结果:
    @FetchRequest(实体:Task.entity(),SortDescriptor:[NSSortDescriptor(键:“pinted”,升序:false),
    NSSortDescriptor(键:“已创建”,升序:true),
    NSSortDescriptor(键:“名称”,升序:true)])
    var任务:FetchedResults
    //当我们创建一个新任务并通过编程导航到它时
    @状态变量selectionId:字符串?
    @任务状态:任务?
    var body:一些观点{
    列表(){
    ForEach(tasks,id:\.self){task in
    导航链接(目标:DetailsView(任务:任务),标记:task.id!.uuiString,选择:self.$selectionId){
    HStack(){
    VStack(对齐:。前导){
    文本(\(task.name??“未知”))
    .font(font.headline.weight(.light))
    .padding(.bottom,5)
    文本(“已创建:\t\(task.Created??Date(),格式化程序:Self.dateFormatter)”)
    .font(字体、副标题、重量(.light))
    .padding(.bottom,5)
    如果task.due!=nil{
    文本(“到期:\t\t\(task.Due!,格式化程序:Self.dateFormatter)”)
    .font(字体、副标题、重量(.light))
    .padding(.bottom,5)
    }
    }
    }
    }
    }
    }
    .navigationBarTitle(文本(“任务”),显示模式:。内联)
    .navigationBarItems(尾部:右按钮)
    }
    var rightButton:一些视图{
    图像(系统名称:“plus.circle”)
    .foregroundColor(颜色(UIColor.systemBlue))
    .font(.title)
    .contentShape(矩形())
    .ontapsigne{
    //创建新任务并导航到其详细视图以添加值
    创建(在:self.viewContext中){(任务,成功,错误)在
    如果成功{
    self.newTask=任务
    self.selectionId=task!.id!.uuistring
    }
    }
    }
    }
    }
    结构细节视图:视图{
    //NSManagedObjectContext
    @环境(\.managedObjectContext)变量viewContext
    @ObservedObject变量任务:任务
    @状态变量名称:String=“”
    @状态变量dueDate:日期=日期()
    @状态变量hasDueDate:Bool=false
    @状态变量isPinned:Bool=false
    var body:一些观点{
    列表(){
    第()节{
    切换(isOn:self.$ispined){
    文本(“固定”)
    }
    }
    第()节{
    文本字段(“名称”,文本:self.$Name)
    .font(font.headline.weight(.light))
    文本(\(task.id?.uuidString??“未知”))
    .font(font.headline.weight(.light))
    }
    第()节{
    HStack(){
    文本(“已创建”)
    垫片()
    文本(\(task.created??Date(),格式化程序:Self.dateFormatter)”)
    .font(字体、副标题、重量(.light))
    }
    切换(isOn:self.$hasDueDate){
    文本(“设定到期日”)
    }
    如果self.hasDueDate{
    日期选择器(“到期日”,选择:self.$dueDate