实施核心数据的问题
Issues implementing Core Data
太棒了,我又带着另一个可能很愚蠢的问题回来了,哈哈。我正在扩展我在 YouTube 教程中找到的直升机游戏作为学习练习。我坚持的部分是我正在尝试使用核心数据来允许用户在他们死后保存他们的分数,并在可从游戏主屏幕访问的单独视图中调用这些保存的分数(我正在使用 ContentView
用于主屏幕,单独的 GameView
用于实际游戏)。
在我的数据模型中,我定义了一个实体“Scores”,带有一个属性“name”。
GameView
文件 returns 中的核心数据用法没有错误,但是当我尝试在要列出分数的视图中获取数据时,出现错误:
Value of type 'FetchedResults' has no member 'name'
这里是 GameView
的代码:
//
// GameView.swift
// Helicopter
//
// Created by Instinct on 10/18/21.
//
import SwiftUI
import AVKit
import CoreData
struct GameView: View{
@State var audioPlayer: AVAudioPlayer!
@State private var heliPosition = CGPoint(x:100, y:100)
@State private var obstPosition = CGPoint(x:1000, y:300)
@State private var flatObstPosition = CGPoint(x:1000, y: 100)
@State var timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
@State var isPaused = false
@State private var score = 0
@Environment(\.managedObjectContext) var context
// @FetchRequest(entity: Scores.entity(), sortDescriptors: []) var scores: FetchedResults<Scores>
@State public var newScoreValue = ""
@State public var selectedScore: Scores?
let groundPosition = CGPoint(x: 0, y: 1000)
func gravity(){
withAnimation{
self.heliPosition.y += 50
}
}
func obstMove(){
if self.obstPosition.x > 0
{
withAnimation{
self.obstPosition.x -= 20
}
}
else
{
self.obstPosition.x = 1000
self.obstPosition.y = CGFloat.random(in: 0...500)
}
}
func flatObstMove(){
if self.flatObstPosition.x > 0
{
withAnimation{
self.flatObstPosition.x -= 20
}
}
else
{
self.flatObstPosition.x = 1000
self.flatObstPosition.y = CGFloat.random(in: 0...500)
}
}
func pause(){
self.timer.upstream.connect().cancel()
// self.isPaused = true
}
func restart(){
self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
self.obstPosition.x = 1000
self.flatObstPosition.x = 1000
self.heliPosition = CGPoint(x:100, y:100)
self.isPaused = false
self.score = 0
}
func resume(){
self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
self.obstPosition.x = 1000
self.flatObstPosition.x = 1000
self.heliPosition = CGPoint(x:100, y:100)
self.isPaused = false
}
func collisionDetection(){
if abs(heliPosition.x - obstPosition.x) < (25 + 20) && abs(heliPosition.y - obstPosition.y) < (25 + 100){
pause()
self.isPaused = true
// self.score = 0
}
if abs(heliPosition.x - flatObstPosition.x) < (25 + 150) && abs(heliPosition.y - flatObstPosition.y) < (25 + 30){
pause()
self.isPaused = true
// self.score = 0
}
}
// func startGame(){
// self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
// self.obstPosition.x = 1000
// self.heliPosition = CGPoint(x:100, y:100)
// self.isPaused = false
// self.score = 0
// }
func fallOffDetection(){
if abs(heliPosition.y) > 800{
pause()
self.isPaused = true
}
}
func levelDetection(){
// if self.isPaused == true{
// self.timer.upstream.connect().cancel()
// }
if self.score >= abs(100){
self.timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
}
else if self.score >= abs(300){
self.timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()
}
else{
// Text("Level 1")
self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
}
}
func save(scores: Scores?) {
if self.selectedScore == nil {
let newScore = Scores(context: self.context)
newScore.name = newScoreValue
try? self.context.save()
}
// else {
// context.performAndWait {
// scores!.name = self.newScoreValue
// try? context.save()
// self.newScoreValue = ""
// self.selectedScore = nil
// }
// }
}
var body: some View {
GeometryReader{ geo in
ZStack{
Helicopter()
.position(self.heliPosition)
.onReceive(self.timer){_ in
self.gravity()
// self.levelDetection()
}
Obstacle()
.position(self.obstPosition)
.onReceive(timer){_ in
// withAnimation{
self.obstMove()
// }
}
FlatObstacle()
.position(self.flatObstPosition)
.onReceive(timer){_ in
self.flatObstMove()
}
Ground()
.position(self.groundPosition)
self.isPaused ? HStack {
Spacer()
Button("restart"){self.restart()}.font(.system(size: 40))
Spacer()
Button("resume"){self.resume()}.font(.system(size:40))
Spacer()
Button("save score"){self.save(scores: selectedScore)}
}: nil
self.isPaused ? Text("your final score is " + String(self.score)) .position(x: geo.size.width / 2, y: geo.size.height - 700).foregroundColor(Color.white).font(.system(size: 50)): nil
TextField("\(self.score)", text: $newScoreValue)
.foregroundColor(.white)
.position(x: geo.size.width - 100, y: geo.size.height / 10)
.multilineTextAlignment(.trailing)
// Spacer()
Ground()
// .frame(width: 1000, height: 200)
.foregroundColor(Color.blue)
}
// .onAppear {
// let sound = Bundle.main.path(forResource: "Calboy - All Night Long_2", ofType: "mp3")
// self.audioPlayer = try! AVAudioPlayer( contentsOf: URL(fileURLWithPath: sound!))
//
// }
// }
.frame(width: geo.size.width, height: geo.size.height)
.background(Color.black)
.gesture(
DragGesture()
.onChanged{ value in
withAnimation{
// self.heliPosition.x = heliPosition.x
self.heliPosition.y = value.location.y
}
}
.onEnded{_ in
self.levelDetection()
})
// TapGesture()
// .onEnded{
// withAnimation{
// self.heliPosition.y -= 100
// }
// })
// .onReceive(self.timer){_ in
// self.levelDetection()
// }
}
.onReceive(self.timer) {_ in
self.collisionDetection()
self.score += 1
}
.onReceive(self.timer){_ in
self.fallOffDetection()
// self.score = 0
}
.edgesIgnoringSafeArea(.all)
}
}
struct GameView_Previews: PreviewProvider {
static var previews: some View {
GameView()
}
}
这是分数列表的代码
//
// ViewScores.swift
// Helicopter
//
// Created by Instinct on 10/19/21.
//
import SwiftUI
import CoreData
struct ScoresList: View{
@Environment(\.managedObjectContext) var context
@FetchRequest(entity: Scores.entity(), sortDescriptors: []) var scores: FetchedResults<Scores>
// @State public var newScoreValue = ""
// @State public var selectedScore: Scores?
// @State private var newScoreValue = ""
// @State private var selectedScore: Scores?
// func load(scores: Scores?) {
// if self.selectedScore == nil {
// let newScore = Scores(context: self.context)
// newScore.name = newScoreValue
// try? self.context.()
// }
var body: some View{
List{
ForEach(scores, id: \.self) { score in
Text("\(scores.name!)")
// .onTapGesture {
// self.newScore = savedScores.name
// self.selectedScore = savedScores
// }
}
}
}
}
为了以防万一,这里是我的 AppDelegate
文件中的代码:
//
// AppDelegate.swift
// CoreDataCRUD
//
// Created by Krassimir Iankov on 8/7/20.
// Copyright © 2020 Krassimir Iankov. All rights reserved.
//
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Scores")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
我又一次搜索了 Internet 和文档,但还是无法弄清楚 smfh。非常感谢任何帮助!
让您的文本使用 ForEach 中的变量
Text("\(score.name!)")
注意单数,复数就是数组
太棒了,我又带着另一个可能很愚蠢的问题回来了,哈哈。我正在扩展我在 YouTube 教程中找到的直升机游戏作为学习练习。我坚持的部分是我正在尝试使用核心数据来允许用户在他们死后保存他们的分数,并在可从游戏主屏幕访问的单独视图中调用这些保存的分数(我正在使用 ContentView
用于主屏幕,单独的 GameView
用于实际游戏)。
在我的数据模型中,我定义了一个实体“Scores”,带有一个属性“name”。
GameView
文件 returns 中的核心数据用法没有错误,但是当我尝试在要列出分数的视图中获取数据时,出现错误:
Value of type 'FetchedResults' has no member 'name'
这里是 GameView
的代码:
//
// GameView.swift
// Helicopter
//
// Created by Instinct on 10/18/21.
//
import SwiftUI
import AVKit
import CoreData
struct GameView: View{
@State var audioPlayer: AVAudioPlayer!
@State private var heliPosition = CGPoint(x:100, y:100)
@State private var obstPosition = CGPoint(x:1000, y:300)
@State private var flatObstPosition = CGPoint(x:1000, y: 100)
@State var timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
@State var isPaused = false
@State private var score = 0
@Environment(\.managedObjectContext) var context
// @FetchRequest(entity: Scores.entity(), sortDescriptors: []) var scores: FetchedResults<Scores>
@State public var newScoreValue = ""
@State public var selectedScore: Scores?
let groundPosition = CGPoint(x: 0, y: 1000)
func gravity(){
withAnimation{
self.heliPosition.y += 50
}
}
func obstMove(){
if self.obstPosition.x > 0
{
withAnimation{
self.obstPosition.x -= 20
}
}
else
{
self.obstPosition.x = 1000
self.obstPosition.y = CGFloat.random(in: 0...500)
}
}
func flatObstMove(){
if self.flatObstPosition.x > 0
{
withAnimation{
self.flatObstPosition.x -= 20
}
}
else
{
self.flatObstPosition.x = 1000
self.flatObstPosition.y = CGFloat.random(in: 0...500)
}
}
func pause(){
self.timer.upstream.connect().cancel()
// self.isPaused = true
}
func restart(){
self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
self.obstPosition.x = 1000
self.flatObstPosition.x = 1000
self.heliPosition = CGPoint(x:100, y:100)
self.isPaused = false
self.score = 0
}
func resume(){
self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
self.obstPosition.x = 1000
self.flatObstPosition.x = 1000
self.heliPosition = CGPoint(x:100, y:100)
self.isPaused = false
}
func collisionDetection(){
if abs(heliPosition.x - obstPosition.x) < (25 + 20) && abs(heliPosition.y - obstPosition.y) < (25 + 100){
pause()
self.isPaused = true
// self.score = 0
}
if abs(heliPosition.x - flatObstPosition.x) < (25 + 150) && abs(heliPosition.y - flatObstPosition.y) < (25 + 30){
pause()
self.isPaused = true
// self.score = 0
}
}
// func startGame(){
// self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
// self.obstPosition.x = 1000
// self.heliPosition = CGPoint(x:100, y:100)
// self.isPaused = false
// self.score = 0
// }
func fallOffDetection(){
if abs(heliPosition.y) > 800{
pause()
self.isPaused = true
}
}
func levelDetection(){
// if self.isPaused == true{
// self.timer.upstream.connect().cancel()
// }
if self.score >= abs(100){
self.timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
}
else if self.score >= abs(300){
self.timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()
}
else{
// Text("Level 1")
self.timer = Timer.publish(every: 0.2, on: .main, in: .common).autoconnect()
}
}
func save(scores: Scores?) {
if self.selectedScore == nil {
let newScore = Scores(context: self.context)
newScore.name = newScoreValue
try? self.context.save()
}
// else {
// context.performAndWait {
// scores!.name = self.newScoreValue
// try? context.save()
// self.newScoreValue = ""
// self.selectedScore = nil
// }
// }
}
var body: some View {
GeometryReader{ geo in
ZStack{
Helicopter()
.position(self.heliPosition)
.onReceive(self.timer){_ in
self.gravity()
// self.levelDetection()
}
Obstacle()
.position(self.obstPosition)
.onReceive(timer){_ in
// withAnimation{
self.obstMove()
// }
}
FlatObstacle()
.position(self.flatObstPosition)
.onReceive(timer){_ in
self.flatObstMove()
}
Ground()
.position(self.groundPosition)
self.isPaused ? HStack {
Spacer()
Button("restart"){self.restart()}.font(.system(size: 40))
Spacer()
Button("resume"){self.resume()}.font(.system(size:40))
Spacer()
Button("save score"){self.save(scores: selectedScore)}
}: nil
self.isPaused ? Text("your final score is " + String(self.score)) .position(x: geo.size.width / 2, y: geo.size.height - 700).foregroundColor(Color.white).font(.system(size: 50)): nil
TextField("\(self.score)", text: $newScoreValue)
.foregroundColor(.white)
.position(x: geo.size.width - 100, y: geo.size.height / 10)
.multilineTextAlignment(.trailing)
// Spacer()
Ground()
// .frame(width: 1000, height: 200)
.foregroundColor(Color.blue)
}
// .onAppear {
// let sound = Bundle.main.path(forResource: "Calboy - All Night Long_2", ofType: "mp3")
// self.audioPlayer = try! AVAudioPlayer( contentsOf: URL(fileURLWithPath: sound!))
//
// }
// }
.frame(width: geo.size.width, height: geo.size.height)
.background(Color.black)
.gesture(
DragGesture()
.onChanged{ value in
withAnimation{
// self.heliPosition.x = heliPosition.x
self.heliPosition.y = value.location.y
}
}
.onEnded{_ in
self.levelDetection()
})
// TapGesture()
// .onEnded{
// withAnimation{
// self.heliPosition.y -= 100
// }
// })
// .onReceive(self.timer){_ in
// self.levelDetection()
// }
}
.onReceive(self.timer) {_ in
self.collisionDetection()
self.score += 1
}
.onReceive(self.timer){_ in
self.fallOffDetection()
// self.score = 0
}
.edgesIgnoringSafeArea(.all)
}
}
struct GameView_Previews: PreviewProvider {
static var previews: some View {
GameView()
}
}
这是分数列表的代码
//
// ViewScores.swift
// Helicopter
//
// Created by Instinct on 10/19/21.
//
import SwiftUI
import CoreData
struct ScoresList: View{
@Environment(\.managedObjectContext) var context
@FetchRequest(entity: Scores.entity(), sortDescriptors: []) var scores: FetchedResults<Scores>
// @State public var newScoreValue = ""
// @State public var selectedScore: Scores?
// @State private var newScoreValue = ""
// @State private var selectedScore: Scores?
// func load(scores: Scores?) {
// if self.selectedScore == nil {
// let newScore = Scores(context: self.context)
// newScore.name = newScoreValue
// try? self.context.()
// }
var body: some View{
List{
ForEach(scores, id: \.self) { score in
Text("\(scores.name!)")
// .onTapGesture {
// self.newScore = savedScores.name
// self.selectedScore = savedScores
// }
}
}
}
}
为了以防万一,这里是我的 AppDelegate
文件中的代码:
//
// AppDelegate.swift
// CoreDataCRUD
//
// Created by Krassimir Iankov on 8/7/20.
// Copyright © 2020 Krassimir Iankov. All rights reserved.
//
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Scores")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
我又一次搜索了 Internet 和文档,但还是无法弄清楚 smfh。非常感谢任何帮助!
让您的文本使用 ForEach 中的变量
Text("\(score.name!)")
注意单数,复数就是数组