求助:如何创建含线条/曲线的自定义PKDrawing文件
创建自定义PKDrawing文件的实现思路与技巧
核心前提:.drawing是PencilKit专属序列化格式
你用随机JSON转.drawing失败的原因很明确:.drawing不是普通JSON,它是PencilKit定义的结构化格式,必须包含version、strokes(每个stroke含points、drawingTool等必填字段),随便生成的JSON不符合规范,自然无法解析。
实现思路
1. 代码直接生成PKDrawing(灵活可控)
PKDrawing本质是[PKStroke]数组,每个PKStroke由一系列PKStrokePoint组成(包含位置、时间、压力、笔触参数)。你可以手动构建这些数据,生成任意线条/曲线:
- 基础流程:定义笔触样式(
PKInk)→ 创建点序列 → 生成Stroke → 组合成Drawing - 适合批量生成标准化图形(比如你代码里的字母库)
示例代码:
// 生成带手绘质感的曲线 func createCurveDrawing() -> PKDrawing { // 用贝塞尔曲线生成平滑路径 let bezierPath = UIBezierPath() bezierPath.move(to: CGPoint(x: 30, y: 100)) bezierPath.addCurve(to: CGPoint(x: 270, y: 100), controlPoint1: CGPoint(x: 100, y: 20), controlPoint2: CGPoint(x: 200, y: 180)) // 将路径转为PKStrokePoint数组,添加随机压力模拟手绘 let points = stride(from: 0.0, to: 1.0, by: 0.02).map { t in let point = bezierPath.point(at: t) let randomForce = Double.random(in: 0.4...0.8) return PKStrokePoint( location: point, timeOffset: t * 0.5, size: CGSize(width: 2, height: 2), opacity: 1, force: randomForce, azimuth: 0, altitude: 0 ) } let stroke = PKStroke( ink: PKInk(.pen, color: .systemBlue), points: points, transform: .identity ) return PKDrawing(strokes: [stroke]) }
2. 导出手绘.drawing文件作为模板
如果需要更自然的手绘风格,直接用PencilKit画布手绘后导出合法文件:
- 写一个极简的PencilKit测试App,或用系统Notes app手绘后分享导出.drawing文件
- 将导出的文件加入项目资源,用
PKDrawing(data:)加载即可 - 这种方式生成的文件完全符合格式规范,不会出现解析失败问题
3. 正确序列化/反序列化JSON
如果需要手动处理JSON,必须通过PencilKit API生成合法结构:
- 先通过代码或手绘得到PKDrawing,再用
drawing.dataRepresentation()转成Data(这就是.drawing文件的本质) - 若要查看JSON结构,可将Data转成JSON字符串分析,但不要手动修改核心字段
- 示例:保存/加载.drawing文件
// 保存PKDrawing到本地 func saveDrawing(_ drawing: PKDrawing, filename: String) { let data = drawing.dataRepresentation() let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("\(filename).drawing") try? data.write(to: url) } // 从本地加载PKDrawing func loadDrawing(filename: String) -> PKDrawing? { let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("\(filename).drawing") guard let data = try? Data(contentsOf: url) else { return nil } return try? PKDrawing(data: data) }
实用技巧
- 优化线条流畅度:代码生成点时,用贝塞尔曲线插值生成密集点,避免线条生硬;
- 模拟真实手绘:给
PKStrokePoint的force、azimuth参数添加随机值,让生成的线条更接近真实笔触; - 批量资源管理:像你的
TextGenerator那样,提前预加载所有.drawing资源,初始化时验证有效性,避免运行时崩溃; - 复用Stroke模板:将常用的线条/曲线保存成模板Drawing,后续通过复制、变换(平移/缩放)快速生成变体。
内容的提问来源于stack exchange,提问作者stackTry
相关产品推荐
相关产品推荐

