如何将对象传递到 SwiftUI 工作表中呈现的视图

问题描述 投票:0回答:1

我有一个非常基本的应用程序,可以让我将玩家添加到列表中并针对它们存储一些值。数据存储在CoreData中,然后显示在一个List中。一切正常,但我希望能够点击列表中的行,在工作表中显示一个新视图,然后编辑现有条目并将其保存回现有条目顶部的 CoreData。

我真的很难访问我选择的播放器并访问它的属性并将其保存回来。

我的播放器结构如下所示:

struct Player: Identifiable {
    let id = UUID()
    let firstName: String
    let lastName: String
    let teamColor: String
    let startingWeight: Double
    let completionWeight: Double
}

我目前在列表中使用

onTapGesture
来显示新视图。我有一个 @State 变量来处理选定的玩家:

@State private var selectedPlayer: Player?

然后我将它传递给

EditPlayerView
进入
@Binding var person: Player

当我打印出

person
变量时,它具有我已通过的正确对象,但是当我尝试访问其属性(.firstName、.lastName 等)时,出现错误:

Cannot use instance member 'person' within property initializer; property initializers run before 'self' is available

所以我不得不将所有内容都放入一个计算属性中,这感觉不对:

var playerFirstName: String {
        return "\(person.firstName)"
        }

自 2014 年以来,我就没有用 Objective-C 接触过 iOS 的东西,在 Objective-C 中上下传递东西相对容易我的播放器对象并轻松访问它。

任何帮助将不胜感激

(我没有为我创建播放器等添加完整代码,因为这一切都按预期工作,但如果有帮助可以添加更多)

我已经尝试过问题中提到的内容 - 使用 Binding 和 State 传递对象并期望能够在工作表中显示的新视图中简单地访问

person.firstName
等但我不能

更新:

我正在为上下文和帮助添加额外的代码。我的 EditPlayerView:

    struct EditPlayerView: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    @Environment(\.presentationMode) var presentationMode
    
    @Binding var person: Player
            
    var playerFirstName: String {
        return "\(person.firstName)"
        }
    
    var playerLastName: String {
        return "\(person.lastName)"
        }
    
    var playerStartWeight: String {
        return "\(person.startingWeight)"
        }
    
    var playerCompletionWeight: String {
        return "\(person.completionWeight)"
        }
    
    var playerDetails: String {
        return "First Name: \(person.firstName)\nLast Name: \(person.lastName)\nStarting Weight: \(person.startingWeight)\nCompletion Weight: \(person.completionWeight)"
    }
    
    // State Variables
    @State private var selectedColor = Color.white
    @State private var amendedFirstName = ""
    @State private var amendedLastName = ""
    @State private var amendedStartWeight = ""
    @State private var amendedCompletionWeight = ""
             
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("Existing Player Details")) {
                    Text(playerDetails)
                }
                Section(header: Text("Edit Details")) {
                    TextField("First Name", text: $amendedFirstName)
                    TextField("Last Name", text: $amendedLastName)
                    TextField("Start Weight", text: $amendedStartWeight)
                    TextField("Completion Weight", text: $amendedCompletionWeight)
                }
                Section {
                    Button(action: {
                        // TODO: Save the updated details to CD. 
                    }) {
                        Text("Save Updated Details")
                    }
                }
            }
            .navigationTitle("Edit Player")
        }
    }
}

添加玩家的主视图的额外编辑:

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \PlayerEntity.lastName, ascending: true)], animation: .default)
    
    private var players: FetchedResults<PlayerEntity>

    @State private var showingAddPlayerView = false
    @State private var showAlert = false
    
    @State private var alertTitle = ""
    @State private var alertMessage = ""
    @State private var alertButtonText = ""
    
    //NEW CODE
    @State private var selectedPlayer: Player?

    var body: some View {
        NavigationView {
            List {
                Section(header: Text("Add New Player")) {
                    Button(action: {
                       
                        showingAddPlayerView = true
                    }) {
                        Text("Add New Player")
                    }
                } // End of Section
                
                Section(header: Text("Participating Players")) {
                    /* Iterating through CoreData to create a "player" for each
                     record that we have */
                    ForEach(players, id: \.id) { playerEntity in
                                            let player = Player(firstName: playerEntity.firstName ?? "",
                                                                lastName: playerEntity.lastName ?? "",
                                                                teamColor: playerEntity.teamColor ?? "",
                                                                startingWeight: Double(playerEntity.startingWeight),
                                                                completionWeight: Double(playerEntity.completionWeight))
                        /*NavigationLink(destination: EditPlayerView()) {
                                        Text("Edit Player Details")
                                    }*/
                        AddPlayerPlayerRow(player: player)
                            .onTapGesture {
                                print(player)
                                //showAlert = true
                                self.alertTitle = "Player Detals"
                                self.alertMessage = "Player: \(player.firstName) \(player.lastName)\nStart Weight: \(player.startingWeight)\nCompletion Weight: \(player.completionWeight)\nTeam Colour: \(player.teamColor)"
                                self.alertButtonText = "Dismiss"
                                
                                // Pass the tapped on player object into the selectedPlayer binding variable
                                // This is so we can use it in our new view
                                selectedPlayer = player
                                
                                
                            }
                            .sheet(item: $selectedPlayer) { person in
                                EditPlayerView(person: Binding.constant(person))
                            }
                            .alert(isPresented: $showAlert) {
                                        Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text(alertButtonText)))
                                    }
                            
                    } // End of ForEach
                    .onDelete(perform: deletePlayers)
                } // End of Section
                
                #if DEBUG
                Section(header: Text("DEBUG")) {
                    Button("Print Data") {
                                    for player in players {
                                        print(player.firstName ?? "")
                                        print(player.lastName ?? "")
                                        print(player.teamColor ?? "")
                                        print(player.startingWeight)
                                        print(player.completionWeight)
                                    }
                                }
                } // End of Section
                #endif
                
            } // End of List
            .listStyle(GroupedListStyle())
            .navigationTitle("MvF North Tyneside")
            .navigationBarItems(trailing: EditButton())
        } // End of NavigationView
        .sheet(isPresented: $showingAddPlayerView) {
            AddPlayerView()
                .environment(\.managedObjectContext, viewContext)
        }
    }
    
    // ############### END OF MAIN CONTENT VIEW ##########################
    
    // ############### START OF ADD NEW PLAYER VIEW ######################
    
    // This is shown when we press the "Add New Player" button
    struct AddPlayerView: View {
        @Environment(\.managedObjectContext) private var viewContext
        @Environment(\.presentationMode) var presentationMode

        // State Variables
        @State private var selectedColor = Color.white
        @State private var playersFirstName = ""
        @State private var playersLastName = ""
        @State private var playersStartingWeight = ""
        @State private var playersCompletionWeight = "0.0"
    
        @State private var showAlert = false
        
        var body: some View {
            NavigationView {
                Form {
                    Section(header: Text("Personal Information")) {
                        TextField("First Name", text: $playersFirstName)
                        TextField("Last Name", text: $playersLastName)
                        TextField("Starting Weight", text: $playersStartingWeight)
                            .keyboardType(.decimalPad)
                        TextField("Completion Weight", text: $playersCompletionWeight)
                            .keyboardType(.decimalPad)
                    } // End of Section
                    Section(header: Text("Team Colour")) {
                        CustomColorPicker(selectedColor: $selectedColor)
                    } // End of Section
                    Section {
                        Button(action: {
                            if playersLastName.isEmpty || playersLastName.isEmpty || playersStartingWeight.isEmpty || playersCompletionWeight.isEmpty {
                                showAlert = true
                                
                            } else {
                                savePlayer()
                            }
                        }) {
                            Text("Save Player")
                        
                        }
                        .alert(isPresented: $showAlert) {
                                    Alert(title: Text("Input Error"), message: Text("Please make sure that all fields are filled out"), dismissButton: .default(Text("OK")))
                                }
                    }
                }
                .navigationTitle("Add Player")
            } // End of Navigation View
        }

         private func savePlayer() {
             
            guard let startingWeight = Double(playersStartingWeight) else { return }
            guard let completionWeight = Double(playersCompletionWeight) else { return }
            
            // Let's get a hex string from our selected color
            let uiColor = UIColor(selectedColor)
            let hexStringFromChosenColour = uiColor.hexString
    
            /* What is entered into the Add Player form is then mapped
             to the CoreData PlayerEntity model */
            let newPlayer = PlayerEntity(context: viewContext)
            newPlayer.id = UUID()
            newPlayer.firstName = playersFirstName
            newPlayer.lastName = playersLastName
            newPlayer.teamColor = hexStringFromChosenColour
            newPlayer.startingWeight = startingWeight
            newPlayer.completionWeight = completionWeight

            do {
                try viewContext.save()
                presentationMode.wrappedValue.dismiss()
            } catch {
                let nsError = error as NSError
                print("[DEBUG] Error saving: \(error.localizedDescription)")
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }

    private func deletePlayers(offsets: IndexSet) {
        withAnimation {
            offsets.map { players[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                print("[DEBUG] Error deleting: \(error.localizedDescription)")
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}
ios swift swiftui core-data binding
1个回答
0
投票

删除 Player 结构并使用自动生成的模型类或使用模型编辑器生成一个,这样您就可以:

class Player: NSManagedObject {

并在

EditPlayerView
中定义为:

@ObservedObject player: Player
© www.soinside.com 2019 - 2024. All rights reserved.