存储包含Core Data数据库中的CGPoints的数组(Swift)

时间:2015-02-28 01:35:04

标签: ios arrays swift core-data cgpoint

因为标题已经说明我正在尝试将数组保存到数据库中。如果可以,我该怎么办?如果不是,我希望你可以帮助我解决其他问题。

我正在创建一个iOS应用程序,如果用户触摸(并移动)屏幕,我将此数据存储在一个数组中。因为它需要多点触摸,所以一次触摸的CGPoints(touchesBegan或touchesMoved)存储在一个数组中,该数组再次存储在主数组中。导致var everyLocation = [[CGPoint]]()。我已经发现不可能直接在数据库中存储CGPoint,因此我可以将它们转换为NSStringFromCGPoint(pointVariable)的字符串。然而,只要我无法存储数组,这就不是很有用了...... 我想存储它发生的日期,所以在我的数据库中我创建了具有两个属性的实体'Locations':'locations'和'date'。在最终的应用程序中,实体名称将是用户正在进行的练习的名称(我有大约四个练习,所以有四个实体)。 我见过的大多数示例代码都将CGPoint存储在单独的x和y或一个字符串中。我也许可以这样做,所以我不必存储数组。要做到这一点,我想我必须将属性设置为touche(s)的坐标,实体名称将是日期,db名称将是练习的名称。如果这是唯一的解决方案,如何在运行时创建实体(带属性)?

提前致谢

5 个答案:

答案 0 :(得分:0)

1)添加" Transformable" type属性。

enter image description here

2)Event.h

@interface Event : NSManagedObject

@property (nonatomic, retain) NSArray * absentArray;
@interface AbsentArray : NSValueTransformer

@end

Event.m

@implementation AbsentArray

+ (Class)transformedValueClass
{
    return [NSArray class];
}

+ (BOOL)allowsReverseTransformation
{
    return YES;
}

- (id)transformedValue:(id)value
{
    return [NSKeyedArchiver archivedDataWithRootObject:value];
}

- (id)reverseTransformedValue:(id)value
{
    return [NSKeyedUnarchiver unarchiveObjectWithData:value];
}

@end

3)只需将其用作普通数组

Event *event = //init
event.absentArray = @[1,2,3];
[context save:nil]

只需在swift中更改这些代码。

你可以理解为.swfit结合.h / .m文件。 Objective C有.h作为头文件,其中有许多属性。 .m是蕴涵文件,应该有哪些方法。

例如: .swift

import Foundation
import CoreData

class Event: NSManagedObject {

    @NSManaged var absentArray: AnyObject

}

3)保存:

let appDelegate =
  UIApplication.sharedApplication().delegate as AppDelegate

  let managedContext = appDelegate.managedObjectContext!
  if !managedContext.save(&error) {
  println("Could not save \(error), \(error?.userInfo)")
 } 

答案 1 :(得分:0)

在威廉指出我转向变形的方向后,我终于成功地把各个部分拼凑起来了。我使用本教程来了解如何使用它:http://jamesonquave.com/blog/core-data-in-swift-tutorial-part-1/

答案 2 :(得分:0)

Swift3让它变得无缝, 写一下

y.left

并将属性类型设置为Transformable并将其Custom类设置为

typealias Point = CGPoint

无需任何操作即可为我工作。

答案 3 :(得分:0)

以下是我从警告消息提示的练习中学到的东西: 在某些时候,Core Data 会在指定 nil 时默认使用“NSSecureUnarchiveFromData”,并且包含不支持 NSSecureCoding 的类的可转换属性将变得不可读。

我的应用程序收集了通过使用 Apple Pencil 或手指在屏幕上绘制而创建的一系列点 [CGPoint],并将其存储在 CoreData 中 - 基本上是我称为 Scribble 的东西的核心。为了存储在 CoreData 中,我创建了一个名为“points”的属性并将类型设置为 Transformable。自定义类设置为 [CGPoint]。此外,我将 CodeGen 设置为手动而不是自动的“类定义”选项。当我生成 CoreData 托管对象子类文件时,它会生成一个 +CoreDataClass.swift 文件,感兴趣的关键行是:

@NSManaged 公共变量点:[CGPoint]?

需要注意的是,如果使用自动选项实际上是有问题的,因为生成的文件不知道CGPoint是什么,无法编辑以添加UIKit的导入以查找定义.

在 Apple 开始鼓励安全编码之前,这一直很有效。在下面的代码文件中,我开发了一个 ScribblePoints 对象来处理编码及其关联的数据转换器。

//
//  ScribblePoints.swift
//

import Foundation
import UIKit

public class ScribblePoints: NSObject, NSCoding {
    var points: [CGPoint] = []

    enum Key: String {
        case points = "points"
    }

    init(points: [CGPoint]) {
        self.points = points
    }

    public func encode(with coder: NSCoder) {
        coder.encode(points, forKey: Key.points.rawValue)
    }

    public required convenience init?(coder: NSCoder) {
        if let sPts = coder.decodeObject(of: ScribblePoints.self, forKey: Key.points.rawValue) {
            self.init(points: sPts.points)
        } else {
            return nil
        }
    }
}

extension ScribblePoints : NSSecureCoding {
    public static var supportsSecureCoding = true
}

@available(iOS 12.0, *)
@objc(ScribblePointsValueTransformer)
final class ScribblePointsValueTransformer: NSSecureUnarchiveFromDataTransformer {

    static let name = NSValueTransformerName(rawValue: String(describing: ScribblePointsValueTransformer.self))

    override static var allowedTopLevelClasses: [AnyClass] {
        return [ScribblePoints.self]
    }

    public static func register() {
        let transformer = ScribblePointsValueTransformer()
        ValueTransformer.setValueTransformer(transformer, forName: name)
    }

    override class func allowsReverseTransformation() -> Bool {
        return true
    }

    override func transformedValue(_ value: Any?) -> Any? {
        if let data = value as? Data {
            // Following deprecated at iOS12:
            //    if let data = value as? Data {
            //        if let points = NSKeyedUnarchiver.unarchiveObject(with: data) as? [CGPoint] {
            //            return points
            //        }
            //    }
            do {
                let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
                unarchiver.requiresSecureCoding = false
                let decodeResult = unarchiver.decodeObject(of: [NSArray.self, ScribblePoints.self], forKey: NSKeyedArchiveRootObjectKey)
                if let points = decodeResult as? [CGPoint] {
                    return points
                }
            } catch {
            }
        }
        return nil
    }

    override func reverseTransformedValue(_ value: Any?) -> Any? {
        if let points = value as? [CGPoint] {
            // Following deprecated at iOS12:
            //    let data = NSKeyedArchiver.archivedData(withRootObject: points)
            //    return data
            do {
                let data = try NSKeyedArchiver.archivedData(withRootObject: points, requiringSecureCoding: true)
                return data
            } catch {
            }
        }
        return nil
    }

}

有了上面的内容,我终于可以为 CoreData 中的“points”属性的 Transformer 名称填写 ScribblePointsValueTransformer。

还可以将自定义类从 [CGPoint] 切换到 ScribblePoints。这似乎不会影响代码执行。但是,如果您重新生成 +CoreDataClass.swift 文件,感兴趣的关键行将变为:

@NSManaged 公共变量点:ScribblePoints?

并且当您重新编译时,您将需要更改代码以处理不同的定义。如果您是从头开始,似乎您可能只想使用 ScribblePoints 定义,并避免处理 NSArrays 和 NSPoints 以及您使用 [CGPoint] 以奇怪的方式神奇地遇到的其他东西的麻烦。

以上是 Swift 5。

答案 4 :(得分:0)

当我将旧的 iOS 设备 (iOS9) 连接到 Xcode 时,我的回答出现了一条警告消息。一切正常,但有关未找到值转换器的警告信息令人不安。问题是,如果您使用的是 iOS12+,则之前的答案仅定义并注册了值转换器。为了在早期系统上毫无怨言地工作,需要避免使用 NSSecureUnarchiveFromDataTransformer,而是使用 ValueTransformer,并依赖 NSSecureCoding 一致性来实现您的编码对象。然后您可以在较旧的 iOS 系统上注册您的值转换器。还应该注意的是,transformedValue() 和 reverseTransformedValue() 函数被颠倒了。

最终结果是以下代码。

//
//  ScribblePoints.swift
//

import Foundation
import UIKit


public class ScribblePoints: NSObject, NSCoding {
    var points:[CGPoint] = []

    enum Key: String {
        case points = "points"
    }
    
    init(points: [CGPoint]) {
        self.points = points
    }
    
    public func encode(with coder: NSCoder) {
        coder.encode(points, forKey: Key.points.rawValue)
    }
    
    public required convenience init?(coder: NSCoder) {
        if let sPts = coder.decodeObject(of: ScribblePoints.self, forKey: Key.points.rawValue) {
            self.init(points: sPts.points)
        } else {
            return nil
        }
    }
}

extension ScribblePoints : NSSecureCoding {
    public static var supportsSecureCoding = true
}

@objc(ScribblePointsValueTransformer)
final class ScribblePointsValueTransformer: ValueTransformer {
    
    static let name = NSValueTransformerName(rawValue: String(describing: ScribblePointsValueTransformer.self))
    
    public static func register() {
        let transformer = ScribblePointsValueTransformer()
        ValueTransformer.setValueTransformer(transformer, forName: name)
    }

    override class func transformedValueClass() -> AnyClass {
        return ScribblePoints.self
    }

    override class func allowsReverseTransformation() -> Bool {
        return true
    }

    override func reverseTransformedValue(_ value: Any?) -> Any? {
        if let data = value as? Data {
            do {
                if #available(iOS 11.0, *) {
                    let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
                    unarchiver.requiresSecureCoding = false
                    let decodeResult = unarchiver.decodeObject(of: [NSArray.self, ScribblePoints.self], forKey: NSKeyedArchiveRootObjectKey)
                    if let points = decodeResult as? [CGPoint] {
                        return points
                    }
                } else {
                    // Fallback on earlier versions
                    if let data = value as? Data {
                        if let points = NSKeyedUnarchiver.unarchiveObject(with: data) as? [CGPoint] {
                            return points
                        }
                    }
                }
            } catch {
            }
        }
        return nil
    }

    override func transformedValue(_ value: Any?) -> Any? {
        if let points = value as? [CGPoint] {
            do {
                if #available(iOS 11.0, *) {
                    let data = try NSKeyedArchiver.archivedData(withRootObject: points, requiringSecureCoding: true)
                    return data
                } else {
                    // Fallback on earlier versions
                    let data = NSKeyedArchiver.archivedData(withRootObject: points)
                    return data
                }
            } catch {
            }
        }
        return nil
    }

}

在 CoreData 中,定义事物的方式如下所示。

CoreData Scribble Definition