Core data SwiftUI/Core数据-更新详细视图时在列表视图和详细视图之间进行非自愿导航
我正在使用TabView/NavigationView&NavigationLink以编程方式从列表视图导航到详细视图,当我将布尔属性“pinted”更新为true并在详细视图中保存核心数据实体时,我得到了以下不幸的副作用: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
@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