iOS Transform坐标变化
在使用CGContext时,由于Quartz 2D与UIKit坐标不一致,所以需要对context进行再一次的变化,达到预期的效果。
1. 不同坐标原点介绍
在Quartz 2D中,坐标原点在画布的左下角,而UIKit中,与屏幕坐标一致,以左上角为坐标原点。
如果以(0,0)点为原点绘制F,那么在不同的坐标系就会获得如下的结果。 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中控件的绘制,坐标系变化可以参考下图。 这也是为什么如果直接在获取到的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);
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都需要以左上角为锚点,所以这里有需要做一次转化,锚点影响如下图。 在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。
之后还是由于坐标系绘制是以自身坐标系计算,再做一轮坐标系的翻转和位移来得到最后的结果,类似下图的操作。
这里需要注意,每一次新的变化都是在之前变化的基础上,所以无论是view还是对context的transform做修改都是有顺序的,这一点与矩阵乘法一致,顺序会影响结果。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
「万字图文」史上最姨母级Java继承详解
点击上方蓝字关注我,设为星标,任何转载请滴滴我授权 课程导学 在Java课堂中,所有老师不得不提到面向对象(Object Oriented),而在谈到面向对象的时候,又不得不提到面向对象的三大特征:封装、继承、多态。三大特征紧密联系而又有区别,本课程就带你学习Java的继承。 你可能不知道继承到底有什么用,但你大概率曾有过这样的经历:写Java项目/作业时候创建很多相似的类,类中也有很多相同的方法,做了很多重复的工作量,感觉很臃肿。而合理使用继承就能大大减少重复代码,提高代码复用性。 继承的初相识 学习继承,肯定是先从广的概念了解继承是什么以及其作用,然后才从细的方面学习继承的具体实现细节,本关就是带你先快速了解和理解继承的重要概念。 什么是继承 继承(英语:inheritance)是面向对象软件技术中的一个概念。它使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。 Java语言是非常典型的面向对象的语言,在Java语言中继承就是子类继承父类的属性和方法,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的方法。父类有时也叫基类、超类;子...
- 下一篇
OpenCV图片动态特效显示(一)--展开显示
学更好的别人, 做更好的自己。 ——《微卡智享》 本文长度为3033字,预计阅读8分钟 前言 最近在规划自己的学习路径,大概又有了一个新的方向,正好最近抽着空做一些OpenCV的基础的小练习,图片的动态特效展示就是用了最简单的函数来做了一些效果。 实现效果 代码演示 微卡智享 新建一个项目opencvimgeffect,配置参考《VS2017配置OpenCV通用属性》 向下展开核心代码 for (int i = 1; i < src.rows; ++i) { tmpsrc = src(Rect(0, 0, src.cols, i)); tmpsrc.copyTo(dst); imshow("dst", dst); waitKey(1); } 向下展开的效果 从左向后展开 //从左向右展开 Mat dst2; for (int i = 1; i < src.cols; ++i) { tmpsrc = src(Rect(0, 0, i, src.rows)); tmpsrc.copyTo(dst2); imshow("dst2", dst2); waitKey(1); } 从左...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Red5直播服务器,属于Java语言的直播服务器
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- SpringBoot2全家桶,快速入门学习开发网站教程
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7