如何在Core Data中存储UIView子类或UIBezierPath?
嘿,我之前也碰到过这个坑!你这里的问题其实出在对NSValue的用法理解上——myView as NSValue是行不通的,因为NSValue是用来包装基础数据类型(比如CGRect、CGPoint这类)的,不是直接把UIView整个转成NSValue。咱们分两种情况来解决,看你实际需求是存frame还是整个View:
方案1:只存View的Frame(最简单,不需要自定义Transformer)
如果你的目标只是存储View的frame,那完全不用搞复杂的ValueTransformer,直接处理CGRect就行:
- 先在Core Data模型里,给对应的实体加一个Transformable类型的属性(比如命名为
storedFrame),默认配置就够用,不用额外改Transformer类。 - 存储时,把View的frame转成NSValue再存:
let frameValue = NSValue(cgRect: myView.frame) yourCoreDataEntity.storedFrame = frameValue // 记得保存Core Data上下文 try? context.save() - 读取时,把NSValue转回到CGRect:
if let savedFrameValue = yourCoreDataEntity.storedFrame as? NSValue { myView.frame = savedFrameValue.cgRectValue }
方案2:存整个UIView(需要自定义ValueTransformer)
如果你确实要存储整个UIView(而不只是frame),那才需要用到自定义ValueTransformer,利用UIView遵循的NSCoding协议来归档/解档:
步骤1:创建自定义Transformer类
class ViewArchiveTransformer: ValueTransformer { // 告诉Core Data转换后的类型是Data override class func transformedValueClass() -> AnyClass { return Data.self } // 支持逆向转换(Data转回UIView) override class func allowsReverseTransformation() -> Bool { return true } // 把UIView归档成Data override func transformedValue(_ value: Any?) -> Any? { guard let targetView = value as? UIView else { return nil } do { return try NSKeyedArchiver.archivedData(withRootObject: targetView, requiringSecureCoding: false) } catch { print("归档View失败:\(error.localizedDescription)") return nil } } // 把Data解档回UIView override func reverseTransformedValue(_ value: Any?) -> Any? { guard let viewData = value as? Data else { return nil } do { return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(viewData) as? UIView } catch { print("解档View失败:\(error.localizedDescription)") return nil } } }
步骤2:注册Transformer并配置Core Data
- 在App启动时(比如
AppDelegate的didFinishLaunchingWithOptions里)注册这个Transformer:ValueTransformer.setValueTransformer(ViewArchiveTransformer(), forName: NSValueTransformerName("ViewArchiveTransformer")) - 回到Core Data模型,给实体加一个Transformable类型的属性,在属性检查器里把
Transformer Class改成ViewArchiveTransformer(注意类名要完全匹配)。
步骤3:存储和读取View
// 存储 yourCoreDataEntity.storedView = myView try? context.save() // 读取 if let savedView = yourCoreDataEntity.storedView as? UIView { // 拿到保存的View后可以直接使用 self.view.addSubview(savedView) }
总结一下:如果只是存frame,方案1足够简单;如果要存整个View,再用方案2的自定义Transformer。你之前的错误是试图直接把UIView转成NSValue,这不符合NSValue的设计初衷——它是用来包装基础数据类型的,不是NSObject子类哦。
内容的提问来源于stack exchange,提问作者Todanie
相关产品推荐
相关产品推荐

