V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
zoule
V2EX  ›  问与答

iOS Transform 坐标变化

  •  
  •   zoule · 2020-11-16 15:16:55 +08:00 · 545 次点击
    这是一个创建于 1261 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在使用 CGContext 时,由于 Quartz 2D 与 UIKit 坐标不一致,所以需要对 context 进行再一次的变化,达到预期的效果。

    1.不同坐标原点介绍

    在 Quartz 2D 中,坐标原点在画布的左下角,而 UIKit 中,与屏幕坐标一致,以左上角为坐标原点。

    如果以( 0,0 )点为原点绘制 F,那么在不同的坐标系就会获得如下的结果。 image.png

    2.Quartz 2D 与 UIKit 坐标系转化

    2.1 UIImage 绘制

    在 iOS 的 UI 开发中,以 UIImage 为例,绘制一张图片,设置 image 的 frame 为(0, 0, 320, 320),会得到上图右的画面。

    如果使用如下代码读取 Context 的 transform,可以看到这个 transform 并不是单位矩阵。

    CGRect frame = CGRectMake(0.0, 0.0, 720, 1280); UIGraphicsBeginImageContext(frame.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGAffineTransform contextTransform = CGContextGetCTM(context);;

    这里的 transform 设置会使 Quartz 2D 与 UIKit 的坐标原点重合,也是方便了 UIKit 中控件的绘制,坐标系变化可以参考下图。 image.png

    这也是为什么如果直接在获取到的 context 上调用 CGContextDrawImage 时会得到一个反转的图像的原因。

    但如果使用 UIImage 的 drawInRect 方法,文档是这么写的: Instance Method draw(in:) Draws the entire image in the specified rectangle, scaling it as necessary to fit. Declaration func draw(in rect: CGRect) Parameters rect The rectangle (in the coordinate system of the graphics context) in which to draw the image. Discussion This method draws the entire image in the current graphics context, respecting the image’s orientation setting. In the default coordinate system, images are situated down and to the right of the origin of the specified rectangle. This method respects any transforms applied to the current graphics context, however. This method draws the image at full opacity using the CGBlendMode.normal blend mode.

    也就是说,UIImage 绘制时使用的坐标还是 UIKit 的内部坐标,所以并不需要对坐标系做任何变化,就可以绘制出与 rect 位置相同的图片了,当然这个方法也会根据图片的朝向绘制。

    2.2 CGContextDrawImage 绘制

    在改变 context 的 transform 时,实际上在变化的其实是坐标系本身。

    而调用绘制方法时,使用的还是坐标系内部坐标,所以当我们想基于获取到的 context 绘制一个如 UIKit 显示的图片,我们还需要对绘制前的坐标系做调整。

    //Y 轴翻转 CGContextScaleCTM(context, 1, -1);

    //需要图片原点与左上角对齐,还需 Y 轴向下平移图片高度 CGContextTranslateCTM(context, 0, -imageSize.height);

    //绘制图片 CGContextDrawImage(context, frame, image.CGImage); image.png

    3 transform 锚点变化

    比如图片编辑页面,我们经常能碰到使用手势对图片进行缩放旋转位移等变化,之后生成一张新的图片。

    根据不同的手势回调,我们可以修改 view.transform,使得界面上的 view 产生与手势相应的变化。

    在这里,UIKit 为了方便我们修改 UI 界面,view 的 transform 是以 view 的 center 为锚点的。

    UIView 的 transform 里面的描述是使用 center 来修改 position 。

    Use this property to scale or rotate the view's frame rectangle within its superview's coordinate system. (To change the position of the view, modify the center property instead.) The default value of this property is CGAffineTransformIdentity.

    而很多其他的方法在调用时,transform 都需要以左上角为锚点,所以这里有需要做一次转化,锚点影响如下图。 image.png

    在 UI 界面的修改中,我们可以使用缩放和旋转手势的回调值直接修改 view 的 transform,以及位移的回调来修改 center,便可以达到我们预期的效果。但这个 transform 无法使用在 context 的绘制上,因为坐标系的变化,是以原点为锚点来做的。

    所以针对 context 现有坐标系的位置,锚点在左上角,需要进行一次 transform 的修改。

    根据上图也可以看出,锚点只会对位置信息产生影响,并不会改变缩放和旋转。

    UIImage *image; //初始化图片

    UIView *view;//应用变化的 view,view 的 size 跟 image 要一致保证缩放比例是对的。

    CGAffineTransform transform = view.transform; CGSize imageSize = image.size; transform.tx = view.center.x; transform.ty = view.center.y; transform = CGAffineTransformTranslate(transform, -imageSize.width * 0.5, -imageSize.height * 0.5);

    其中 tx,ty 为锚点在坐标系的位置

    当前的锚点在视图的中心点,我们需要改变到视图的左上角,这样就可以和坐标系原点重合。其中*( imageSize.width * 0.5, imageSize.height * 0.5 )*为锚点在图片中的位置,此时 transform 为锚点在视图左上角时的变化矩阵。

    4.组合 Transform

    在 CGContext 上想得到上图中间的结果,不仅需要应用缩小 1/2 和旋转 45 度的变化,还需要调整。

    之前说过 CGContext 应用旋转是应用在坐标系上,跟视图应用旋转的方式是自身坐标系是一致的。所以当直接获取到的 CGContext,优化后的坐标系也是左上角为原点时,可以直接再 CGContext 上应用我们计算出的 transform 。

    之后还是由于坐标系绘制是以自身坐标系计算,再做一轮坐标系的翻转和位移来得到最后的结果,类似下图的操作。

    image.png

    这里需要注意,每一次新的变化都是在之前变化的基础上,所以无论是 view 还是对 context 的 transform 做修改都是有顺序的,这一点与矩阵乘法一致,顺序会影响结果。

    1 条回复    2020-11-17 09:57:28 +08:00
    chengsitom
        1
    chengsitom  
       2020-11-17 09:57:28 +08:00
    图全挂了,非常影响观看
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2280 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 05:38 · PVG 13:38 · LAX 22:38 · JFK 01:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.