核心数据对象仍然保存在presentationMode SwiftUI中的dismiss/cancel

Posted

技术标签:

【中文标题】核心数据对象仍然保存在presentationMode SwiftUI中的dismiss/cancel【英文标题】:Core Data Object still saved on dismiss/cancel in presentationMode SwiftUI 【发布时间】:2020-01-17 09:24:08 【问题描述】:

当我尝试关闭/取消添加对象模式时,它正在创建一个空对象,而不是仅仅取消。

我已经尝试过 deleteObject、context.rollback() 和一堆其他随机的东西。希望得到一些帮助并能够回答任何问题。

通过将“取消”按钮放在 NavigationBarItem 中,我意识到这不是问题,但希望能够了解如何制作单独的“取消(或关闭)”按钮。

ContentView.swift

import SwiftUI
import CoreData


struct ContentView: View 
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Game.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Game.gameName, ascending: true)]) var games: FetchedResults<Game>
    @State private var showingAddGame = false


    var body: some View 
        GeometryReader  geometry in
            NavigationView 
                List 
                    ForEach(self.games, id: \.self)  games in
                        NavigationLink(destination: GameGoalsDetail(game: games)) 
                            VStack(alignment: .leading) 
                                Text(games.gameName ?? "Unknown Game")
                                Text(games.gameDescription ?? "Unknown Game Description")
                            
                        
                    
                    .onDelete(perform: self.removeGames)
                    

                .navigationBarItems(leading:
                    HStack 
                        Button(action: 
                                self.showingAddGame.toggle()
                            ) 
                                Text("Add Game")
                                    .padding(.top, 50)
                                    .foregroundColor(Color.yellow)
                        .sheet(isPresented: self.$showingAddGame) 
                                AddGameView().environment(\.managedObjectContext, self.moc)
                        
                        Image("Game Goals App Logo")
                        .resizable()
                        .frame(width: 100, height: 100)
                        .padding(.leading, (geometry.size.width / 2.0) + -160)
                        .padding(.bottom, -50)
                    , trailing:
                        EditButton()
                            .padding(.top, 50)
                            .foregroundColor(Color.yellow)
                            )
            
        
    

    func removeGames(at offsets: IndexSet) 
        for index in offsets 
            let game = games[index]
            moc.delete(game)
        
        try? moc.save()
    



struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let newGame = Game(context: context)
        newGame.gameName = "Apex Legends"
        newGame.gameDescription = "Maybe this will work"
        return ContentView().environment(\.managedObjectContext, context)
    

AddGameView.swift

import SwiftUI
import CoreData


struct AddGameView: View 

    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Game.entity(), sortDescriptors: []) var games: FetchedResults<Game>
    @Environment(\.presentationMode) var presentationMode

    @State private var gameName = ""
    @State private var gameDescription = ""
    @State private var showingAlert = false

    var body: some View 
        Form 
            Section 
                TextField("Game Name", text: $gameName)
                TextField("Game Description", text: $gameDescription)
            
            HStack 
                Button("Add Game") 
                    let newGame = Game(context: self.moc)
                    newGame.gameName = self.gameName
                    newGame.gameDescription = self.gameDescription

                    do 
                        try self.moc.save()
                        self.presentationMode.wrappedValue.dismiss()
                     catch 
                        print("Whoops! \(error.localizedDescription)")
                    

                
                Button(action: 
                    self.presentationMode.wrappedValue.dismiss()
                ) 
                    Text("Cancel")
                
                .padding(10)
                .foregroundColor(Color.white)
                .background(Color.red)
            
        
    


struct AddGameView_Previews: PreviewProvider 
    static var previews: some View 
        AddGameView()
    

我已经搜索过所有内容,所以如果我在 *** 帖子中遗漏了一些内容,请将其链接起来,因为我不仅想解决这个问题,还想了解原因。

【问题讨论】:

【参考方案1】:

您的取消按钮未创建空对象。问题是表单中包含“添加”和“取消”按钮的整行是交互式的,并且会触发两个按钮的操作。

我在这里找到了答案:https://***.com/a/59402642/12315994 要保持当前布局,您只需为每个按钮添加一行:

.buttonStyle(BorderlessButtonStyle())

在此之后,只有点击每个按钮才会触发动作。带有按钮的表单行将不可点击。

还有 2 个其他解决方案。两者都是为了将​​您的按钮移出表单。

解决方案 1 是像这样将按钮移动到 NavigationBarItems:

导入 SwiftUI 导入核心数据

struct AddGameView: View 
    
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Game.entity(), sortDescriptors: []) var games: FetchedResults<Game>
    @Environment(\.presentationMode) var presentationMode
    
    @State private var gameName = ""
    @State private var gameDescription = ""
    @State private var showingAlert = false
    
    var body: some View 
        NavigationView 
            VStack 
                Form 
                    Section 
                        TextField("Game Name", text: $gameName)
                        TextField("Game Description", text: $gameDescription)
                    
                    
                
                
            
            .navigationBarItems(
                leading:
                Button(action: 
                    self.presentationMode.wrappedValue.dismiss()
                ) 
                    Text("Cancel")
                
                .padding(10)
                .foregroundColor(Color.white)
                .background(Color.red)
                ,
                
                trailing:
                Button(action: 
                    let newGame = Game(context: self.moc)
                    newGame.gameName = self.gameName
                    newGame.gameDescription = self.gameDescription
                    
                    do 
                        try self.moc.save()
                        self.presentationMode.wrappedValue.dismiss()
                     catch 
                        print("Whoops! \(error.localizedDescription)")
                    
                ) 
                    Text("Add Game")
                
            )
        
        
        
    

解决方案 2 是将按钮移出Form并将它们移动到屏幕底部。像这样:

import SwiftUI
import CoreData


struct AddGameView: View 
    
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Game.entity(), sortDescriptors: []) var games: FetchedResults<Game>
    @Environment(\.presentationMode) var presentationMode
    
    @State private var gameName = ""
    @State private var gameDescription = ""
    @State private var showingAlert = false
    
    var body: some View 
        VStack 
            Form 
                Section 
                    TextField("Game Name", text: $gameName)
                    TextField("Game Description", text: $gameDescription)
                
                
            
            
            HStack 
                Button(action: 
                    let newGame = Game(context: self.moc)
                    newGame.gameName = self.gameName
                    newGame.gameDescription = self.gameDescription
                    
                    do 
                        try self.moc.save()
                        self.presentationMode.wrappedValue.dismiss()
                     catch 
                        print("Whoops! \(error.localizedDescription)")
                    
                    
                ) 
                    Text("Add Game")
                
                
                
                Button(action: 
                    self.presentationMode.wrappedValue.dismiss()
                ) 
                    Text("Cancel")
                
                .padding(10)
                .foregroundColor(Color.white)
                .background(Color.red)
            
            
        
        
        
    

从用户体验的角度来看,这两个选项都比您当前的布局更好,因为按钮现在位于更标准的位置。尤其是版本 1 是在 ios 中呈现此类按钮的更标准方式。

【讨论】:

以上是关于核心数据对象仍然保存在presentationMode SwiftUI中的dismiss/cancel的主要内容,如果未能解决你的问题,请参考以下文章

核心数据保存到数据库不起作用

更改未保存在核心数据模型中(例如删除对象)

如何在核心数据中正确保存可转换对象?

保存核心数据对象后,UISplitView 详细信息未更新

核心数据:保存唯一的对象 ID

iOS - 核心数据 - 保存包含对象数组的对象