我有一个非常基本的应用程序,可以让我将玩家添加到列表中并针对它们存储一些值。数据存储在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)")
}
}
}
}
删除 Player 结构并使用自动生成的模型类或使用模型编辑器生成一个,这样您就可以:
class Player: NSManagedObject {
并在
EditPlayerView
中定义为:
@ObservedObject player: Player