将 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 文件中?使用啥属性类型?

如何将 UIColor 转换为十六进制字符串?

如何将 UIView 存储在 CoreData 或 UserDefaults 中

iOS swift:使用 coredata (cloudkit) 存储缓存

使用内置 CoreData 选项将 blob 存储在外部位置

c_cpp Objective-C类将十六进制字符串转换为UIColor。支持#RGB#ARGB #RRGGBB #AARRGGBBUsage:[UIColor colorWithHexString: