将 UIColor 存储在 CoreData 中
Posted
技术标签:
【中文标题】将 UIColor 存储在 CoreData 中【英文标题】:Store UIColor in CoreData 【发布时间】:2015-05-25 09:18:32 【问题描述】:如何在 64 位 上存储 UIColor 到 CoreData 而不会丢失? 在 32 位上返回正确的 UIColor。
CoreData 设置
属性类型:可变形 NSManagedObject 子类属性:@NSManaged var color: UIColor?颜色值存储前
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
输出例如64 位上的红色:
0.20000000000000018
在 32 位上输出红色
0.199999928
从CoreData检索颜色后
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
输出例如64 位上的红色:
0.20000000298023224
在 32 位上输出红色:
0.199999928
导致的问题
使用 == 的颜色比较在 64 位上失败,因为值略有不同。在 32 位上一切正常,颜色比较成功。
【问题讨论】:
32 位和 64 位浮点数都不能准确存储数字0.2
。在大多数情况下,将浮点数与 ==
进行比较是不好的。
已尝试将颜色值保存为十六进制,以完全避免精度问题。
你是如何归档 UIColor 的?用 float / double 做 == 绝不是一个聪明的主意!查看***.com/questions/1275662/…
【参考方案1】:
将NSKeyedArchiver.archivedDataWithRootObject(color)
分配给数据变量并将其保存到您的Core Data 存储中。
要读取数据,只需将NSKeyedUnarchiver.unarchiveObjectWithData(colorData)
分配给颜色变量。
如果您想知道如何比较浮点数,您可以随时参考this。
【讨论】:
我认为如果属性被定义为“Transformable”,Core Data 运行时会自动进行这些转换。 – 我不确定这是否解决了问题所涉及的 32 位和 64 位浮点问题。 无论如何你都不应该直接比较浮点数。 64 位精度更高,但保存到数据存储时会四舍五入,取出时会留下不同的值。使用 epsilon :) 我将 UIColor 对象与 color == colorRetrievedFromCoreData 进行比较。四舍五入的 CoreData 似乎是问题所在。正如您所建议的,这可以通过将其存储为 NSData 来规避,其中包含 UIColor 对象的编码形式。 如果人们想使用上下文发送 UIColor 来观看,他们将需要这个。使 coreData 属性可转换是存储 UIColor 的另一个选项,但如果不转换为 NSData,您将无法传输它。仅供参考【参考方案2】:这解决了我的问题:
将 CoreData 属性名称设置为我的自定义 NSValueTransformer 子类:MQColorTransformer
实现了以下 NSValueTransformer 子类(它也将 UIColor 转换为 NSData,就像 Schemetrical 建议的那样,但它的优点是我仍然可以将 UIColor 分配给我的颜色属性并且 NSValueTransformer 会小心幕后的转变):
NSValueTransformer 子类
import Foundation
import UIKit
@objc(MQColorTransformer) class MQColorTransformer: NSValueTransformer
override class func transformedValueClass() -> AnyClass
return NSData.classForCoder()
override func transformedValue(value: AnyObject!) -> AnyObject
// Transform UIColor to NSData
let color = value as! UIColor
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
var components:[CGFloat] = [red, green, blue, alpha]
let dataFromColors = NSData(bytes: components,
length: sizeofValue(components)*components.count)
return dataFromColors
override func reverseTransformedValue(value: AnyObject!) -> AnyObject
// Transform NSData to UIColor
let data = value as! NSData
var components = [CGFloat](count: 4, repeatedValue:0)
var length=sizeofValue(components)
data.getBytes(&components, length: length * components.count)
let color = UIColor(red: components[0],
green: components[1],
blue: components[2],
alpha: components[3])
return color
【讨论】:
请注意(除非我弄错了)这种表示与设备无关。如果颜色在 32 位设备上存档并在 64 位设备上检索(反之亦然),结果将是错误的。 您意识到对于第一个函数,您可以return NSKeyedArchiver.archivedDataWithRootObject(color)
并摆脱其余代码。对于第二个函数,您可以return NSKeyedUnarchiver.unarchiveObjectWithData(data)
并摆脱其余代码。它是一种更优雅的解决方案,而不是检索颜色组件。
@Schemetrical :这不起作用,因为在 64 位上比较再次失败。
您不应该直接比较浮点数并依赖相同类型的值进行比较,使用 FLT_EPSILON :)
@Schemetrical: 所以你的意思是检查两种颜色之间的距离是否是例如小于 FLT_EPSILON ?以上是关于将 UIColor 存储在 CoreData 中的主要内容,如果未能解决你的问题,请参考以下文章
如何将 UIImages 和 NSArrays 存储在 CoreData 文件中?使用啥属性类型?
如何将 UIView 存储在 CoreData 或 UserDefaults 中
iOS swift:使用 coredata (cloudkit) 存储缓存
使用内置 CoreData 选项将 blob 存储在外部位置
c_cpp Objective-C类将十六进制字符串转换为UIColor。支持#RGB#ARGB #RRGGBB #AARRGGBBUsage:[UIColor colorWithHexString: