使用SwiftUI导航视图和已排序的FetchRequest在详细信息视图中重新排列列表项时的问题 [英] Issue when rearranging List item in detail view using SwiftUI Navigation View and Sorted FetchRequest

查看:115
本文介绍了使用SwiftUI导航视图和已排序的FetchRequest在详细信息视图中重新排列列表项时的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个NavigationView,其中包含一个列表,该列表显示了来自CoreData FetchRequest的任务。 FetchRequest在Task.dueDate上升序排列。 TaskDetail视图基本上由用于标题的TextField和用于日期的日期选择器组成。在局部视图中更改值是可行的。尽管每次尝试更改日期值时都会出现一些奇怪的行为。日期被更改,但是导航视图自动退出详细信息视图并返回列表视图。仅当我更改日期以使列表因排序而重新排列时,才会发生这种情况。



如何防止上述这种奇怪的行为? ?



  // 
// ContentView.swift

导入SwiftUI
导入CoreData

struct ContentView:View {

@Environment(\.managedObjectContext)var moc
@FetchRequest(fetchRequest:Task.requestAllTask​​s())var任务:FetchedResults< Task>

var主体:某些视图{
NavigationView {
List(tasks,id:\.id)} {
NavigationLink中的任务(目标:TaskDetail(任务: task)){
Text( \(task.title))
}
} .navigationBarTitle( Tasks)。navigationBarItems(trailing:Button( new){self .addTask()})
}
}

func addTask()->无效{
let newTask = Task(context:self.moc)
newTask.id = UUID()
newTask.title =任务\(tasks.count)
newTask.dueDate = Date()
print( created new Task)
if(self.moc.hasChanges){
try? self.moc.save()
print( saved MOC)
}
print(self.tasks)
}

}

struct TaskDetail:View {

@ObservedObject var task:Task

var body:some View {
VStack {
TextField( 名称,文本:$ task.title)
DatePicker( dueDate,选择:$ task.dueDate,displayedComponents:.date)
.labelsHidden()
}
}
}

//
// Task.swift

import Foundation
import CoreData

public class任务:NSManagedObject,可识别{
@NSManaged公共变量ID:UUID?
@NSManaged公共变量到期日期:日期
@NSManaged公共变量标题:字符串

静态函数requestAllTask​​s()-> NSFetchRequest< Task> {
let request:NSFetchRequest< Task> = Task.fetchRequest()为! NSFetchRequest< Task>

let sortDescriptor = NSSortDescriptor(key: dueDate,升序:true)
request.sortDescriptors = [sortDescriptor]

返回请求
}
}

要创建此运行的最小可复制版本,请执行以下操作:


  1. 创建新的Xcode Single View App项目。确保选中
    CoreData复选框。

  2. 复制上面ContentView的代码,然后粘贴/替换到ContentView.swift中。

  3. 创建一个名为Task的新Swift文件。复制Task的代码并粘贴到Task.swift中。

  4. 根据下图在ProjectName.xcdatamodeld中添加实体。

  5. Run



I在Xcode 11.4上。



让我知道您是否需要我提供更多信息。
非常感谢您的帮助!谢谢!

解决方案

UPDATE 2(iOS 14 beta 3)


此问题似乎已在iOS 14 beta 3中修复:进行影响排序顺序的更改时,详细信息视图不再弹出。




更新


Apple似乎将此视为功能,而不是错误;今天他们回复了我对此问题的反馈意见(FB7651251),如下所示:


我们建议使用isActive并使用
自己管理推送如果这是您想要的行为,请进行绑定。就像
的行为正确一样。


这是因为当您
更改排序顺序时,推送视图的身份也会更改。





我在上面的评论中提到,我相信这是 iOS 13.4 中的错误。 / p>

一种解决方法是在列表外部使用NavigationLink并将列表行定义为按钮,


a)设置要编辑的任务(一个新的@State var selectedTask)和


b)触发导航链接到TaskDetail(任务:selectedTask!)。


此设置将取消选定对象的耦合任务从其在排序列表中的位置开始,从而避免了由于编辑DueDate可能引起的重新排序而引起的不当行为。


要实现这一点:


  1. 添加这两个@State变量以构造ContentView


  @State private var selectedTask:任务? 
@State private var linkIsActive = false



  1. 更新内容ContentView的正文如下


  var body:某些视图{
NavigationView {
ZStack {
NavigationLink(
目标:linkDestination(selectedTask:selectedTask),
isActive:self。$ linkIsActive){
EmptyView()
}
List(tasks){
Button中的任务(操作:{
self.selectedTask =任务
self.linkIsActive = true
}){
NavigationLink(目标:EmptyView()){
Text( \(task.title))
}
}
}
}
.navigationBarTitle( Tasks)。navigationBarItems(trailing:Button( new){self.addTask()})
}
}



  1. 将以下结构添加到ContentView.swift


 结构链接目标:视图{
让selectedTask:任务?
变量体:某些视图{
返回组{
if selectedTask!= nil {
TaskDetail(task:selectedTask!)
} else {
EmptyView ()
}
}
}
}


I have a NavigationView with a list showing tasks from a CoreData FetchRequest. The FetchRequest is sorted ascending on Task.dueDate. The TaskDetail view basically consists of a TextField for the title and a date picker for the date. Changing the values in the detail view works. Though I get some weird behaviour every time I try to change the date value. The date gets changed but the Navigation view automatically exits the detail view and goes back to the list view. It only happens when I change the date in such a way that the list gets rearranged due to the sorting.

How do I prevent this weird behaviour described above??

//
//  ContentView.swift

import SwiftUI
import CoreData

struct ContentView: View {

    @Environment(\.managedObjectContext) var moc
    @FetchRequest(fetchRequest: Task.requestAllTasks()) var tasks: FetchedResults<Task>

    var body: some View {
        NavigationView {
            List(tasks, id: \.id) { task in
                NavigationLink(destination: TaskDetail(task: task)) {
                    Text("\(task.title)")
                }
            }.navigationBarTitle("Tasks").navigationBarItems(trailing: Button("new") {self.addTask()})
        }
    }

    func addTask() -> Void {
        let newTask = Task(context: self.moc)
        newTask.id = UUID()
        newTask.title = "task \(tasks.count)"
        newTask.dueDate = Date()
        print("created new Task")
        if (self.moc.hasChanges) {
            try? self.moc.save()
            print("saved MOC")
        }
        print(self.tasks)
    }

}

struct TaskDetail : View {

    @ObservedObject var task: Task

    var body: some View {
        VStack{
            TextField("name", text: $task.title)
            DatePicker("dueDate", selection: $task.dueDate, displayedComponents: .date)
                .labelsHidden()
        }
    }
}

//
//  Task.swift

import Foundation
import CoreData

public class Task: NSManagedObject, Identifiable {
    @NSManaged public var id: UUID?
    @NSManaged public var dueDate: Date
    @NSManaged public var title: String

    static func requestAllTasks() -> NSFetchRequest<Task> {
        let request: NSFetchRequest<Task> = Task.fetchRequest() as! NSFetchRequest<Task>

        let sortDescriptor = NSSortDescriptor(key: "dueDate", ascending: true)
        request.sortDescriptors = [sortDescriptor]

        return request
    }
}

To create a running minimal reproducible version of this...do:

  1. Create new Xcode "Single View App" Project. Make sure to check the CoreData checkbox.
  2. Copy the code for ContentView above and paste/replace in ContentView.swift.
  3. Create a new Swift file named Task. Copy the code for Task and paste in Task.swift.
  4. Add the entities in the ProjectName.xcdatamodeld according to the image below.
  5. Run

I am on Xcode 11.4.

Let me know if you need me to provide more information. Any help is much appreciated! Thanks!

解决方案

UPDATE 2 (iOS 14 beta 3)

The issue seems to be fixed in iOS 14 beta 3: the Detail view does no longer pop when making changes that affect the sort order.


UPDATE

It seems Apple sees this as a feature, not a bug; today they replied to my feedback (FB7651251) about this issue as follows:

We would recommend using isActive and managing the push yourself using the selection binding if this is the behavior you desire. As is this is behaving correctly.

This is because the identity of the pushed view changes when you change the sort order.


As mentioned in my comment above I believe this is a bug in iOS 13.4.

A workaround could be to use a NavigationLink outside of the List and define the List rows as Buttons that

a) set the task to be edited (a new @State var selectedTask) and

b) trigger the NavigationLink to TaskDetail(task: selectedTask!).

This setup will uncouple the selected task from its position in the sorted list thus avoiding the misbehaviour caused by the re-sort potentially caused by editing the dueDate.

To achieve this:

  1. add these two @State variables to struct ContentView

    @State private var selectedTask: Task?
    @State private var linkIsActive = false

  1. update the body of struct ContentView as follows

    var body: some View {
        NavigationView {
            ZStack {
                NavigationLink(
                    destination: linkDestination(selectedTask: selectedTask),
                    isActive: self.$linkIsActive) {
                    EmptyView()
                }
                List(tasks) { task in
                    Button(action: {
                        self.selectedTask = task
                        self.linkIsActive = true
                    }) {
                        NavigationLink(destination: EmptyView()){
                            Text("\(task.title)")
                        }
                    }
                }
            }
            .navigationBarTitle("Tasks").navigationBarItems(trailing: Button("new") {self.addTask()})
        }
    }

  1. add the following struct to ContentView.swift

    struct linkDestination: View {
        let selectedTask: Task?
        var body: some View {
            return Group {
                if selectedTask != nil {
                    TaskDetail(task: selectedTask!)
                } else {
                    EmptyView()
                }
            }
        }
    }

这篇关于使用SwiftUI导航视图和已排序的FetchRequest在详细信息视图中重新排列列表项时的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆