iOS PencilKit绘图应用中PKCanvasView旋转导致PKEraserTool尺寸异常的解决咨询
我最近在开发一款基于PencilKit的iOS绘图应用,遇到了一个挺棘手的问题:当给PKCanvasView添加旋转变换后,橡皮擦工具的尺寸会出现莫名的异常变化,折腾了好一阵都没搞定,来问问大家有没有好的解决办法。
具体的问题表现是这样的:
- 我通过Interface Builder给
PKCanvasView添加了用户自定义运行时属性layer.transform.rotation.z = 0.7(差不多是顺时针旋转45度),结果发现橡皮擦的尺寸直接变大了——哪怕我选的是最小尺寸的橡皮擦,点击屏幕留下的痕迹也比未旋转时大很多 - 有意思的是,如果是逆时针旋转,橡皮擦的尺寸却完全不受影响,一点变化都没有
- 这个问题在Apple官方的PencilKit示例项目里也能100%复现,我啥都没改,就给示例里的
PKCanvasView加了那个旋转变换属性,结果同样出现了橡皮擦变大的情况
给大家对比下两种状态的效果:
未旋转状态:最小尺寸橡皮擦点击后留下的白色点大小完全正常
顺时针旋转45度后:同样的最小尺寸橡皮擦,点击留下的白色点明显被放大了一圈
我试过直接调整橡皮擦的size属性,但旋转后还是会出现拉伸放大的情况,完全不管用。有没有办法让橡皮擦在任意旋转角度下都保持一致的尺寸呢?
我摸索出来的几个解决思路
1. 别直接旋转PKCanvasView,转它的父视图就好了
我发现问题根源可能是直接修改PKCanvasView的layer变换会干扰PencilKit内部的坐标计算逻辑——毕竟PencilKit的工具渲染是基于自身坐标系来的。后来我换了个思路:给PKCanvasView套个父UIView,然后旋转这个父视图,而不是直接动PKCanvasView本身。
操作步骤也很简单:
- 在Interface Builder里给
PKCanvasView加个父UIView,确保父视图和PKCanvasView的尺寸完全匹配 - 把原来加在
PKCanvasView上的layer.transform.rotation.z旋转变换,移到这个父视图上
亲测有效!这样做的话,PKCanvasView自身的坐标系没被改动,PencilKit内部的工具渲染还是按原始逻辑计算尺寸,只是整个绘图区域通过父视图被旋转展示出来,橡皮擦的大小自然就保持一致了,完全没有之前的放大问题。
2. 手动修正坐标系转换(复杂但可行)
如果因为业务需求必须直接旋转PKCanvasView的layer,那可以试试手动修正PencilKit的坐标计算。比如通过PKCanvasViewDelegate的回调方法,或者监听触摸事件,反向计算旋转变换对橡皮擦尺寸的影响,然后调整橡皮擦的有效尺寸。
举个例子,顺时针旋转θ角时,橡皮擦的实际显示尺寸会被放大1 / cosθ倍(45度时cosθ≈0.707,所以放大约1.414倍),这时候可以把橡皮擦的size除以这个缩放因子,来抵消旋转带来的放大效果。不过这个方法需要处理不同旋转角度的情况,还要考虑PencilKit内部的压感、坐标映射等逻辑,测试起来比较麻烦,容易出兼容问题。
3. 自定义橡皮擦工具(不推荐,工作量大)
如果上面两种方法都不适用,还可以考虑自定义一个橡皮擦工具,基于PKTool来实现自己的渲染逻辑。但这种方式需要自己处理触摸事件、路径渲染、压感支持等,工作量巨大,而且很难完全还原PencilKit原生橡皮擦的流畅体验,除非万不得已,不建议这么做。
最终推荐方案
最省心的肯定是第一种——旋转父视图而非直接旋转PKCanvasView,实现简单,完全不改动PencilKit的内部逻辑,而且能完美解决橡皮擦尺寸异常的问题。我自己在Apple的PencilKit示例项目里测试过,用这个方法后,不管怎么旋转,橡皮擦的大小和未旋转时完全一样,完美符合需求。
内容来源于stack exchange




