iOS 16下如何复制UIView?适配废弃API后遇编码报错
问题背景
原本使用以下Swift代码实现UIView的复制功能:
extension UIView { func copyObject<T:NSObject>() throws -> T? { let data = try NSKeyedArchiver.archivedData(withRootObject:self, requiringSecureCoding:false) return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? T } }
适配iOS 16后,代码出现API废弃警告:
'unarchiveObject(with:)' was deprecated in iOS 12.0: Use +unarchivedObjectOfClass:fromData:error: instead
'archivedData(withRootObject:)' was deprecated in iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead
修改警告后的代码如下:
func copyView<T: UIView>() -> T { let data = try? NSKeyedArchiver.archivedData(withRootObject:self, requiringSecureCoding:false) return try! NSKeyedUnarchiver.unarchivedObject(ofClass: UIView.self, from:data!) as! T //return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self)) as! T }
执行复制操作时出现以下错误:
error: Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class 'UIView' does not adopt it." UserInfo={NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class 'UIView' does not adopt it.}
解决方案
问题核心是unarchivedObject(ofClass:from:)强制要求目标类遵循NSSecureCoding,但系统UIView并未实现该协议。改用兼容的解档方法并优化错误处理即可解决:
修正后的代码
extension UIView { func copyView<T: UIView>() throws -> T { // 归档数据,保持关闭安全编码要求 let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) // 使用不强制安全编码的解档方法 guard let copiedView = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? T else { throw NSError(domain: "ViewCopyError", code: -1, userInfo: [NSLocalizedDescriptionKey: "视图复制失败"]) } return copiedView } }
关键说明
- 保留
unarchiveTopLevelObjectWithData(_:):该方法不强制验证NSSecureCoding,适配系统UIView的特性 - 移除不安全的强制解包:用
try替代try?、try!,通过guard做类型校验,配合自定义错误抛出提升代码健壮性 - 明确泛型约束:将泛型约束限定为
UIView,更贴合视图复制的场景
如果是自定义UIView子类需要支持安全编码,可手动遵循NSSecureCoding并实现init?(coder:)、encode(with:)方法,但系统UIView无需额外处理。
内容的提问来源于stack exchange,提问作者tarun5130

