【转】如何在IOS中使用3D UI - CALayer的透视投影
原文网址:http://www.tairan.com/archives/2041/
例子代码可以在 http://www.tairan.com/thread-3607-1-1.html 下载
iOS的UI是基于UIView类的,我们能看到的每个UI元素都是UIView或者UIView的子类。View按树形结构组织起来,树根是UIWindow。
View负责界面的交互和显示,其中显示部分由CALayer来完成。每个UIView包含一个CALayer实例。可以这么认为,UIView本身是不可见的,我们能看到的都是CALayer,UIView只是负责对CALayer进行管理。
UIView的显示设置都是对CALayer属性的封装,但是这层封装掩盖了CALayer提供的3D显示功能。所以我们想让UIView显示3D的效果的话,需要直接操作CALayer。
要操作CALayer对象,首先要在工程中包含QuartzCore.framework,在文件中import <QuartzCore/QuartzCore.h>头文件。QuartzCore.framework中包含了CALayer以及CALayer一些官方子类的定义。
通过设置CALayer的transform属性,可以使CALayer产生3D空间内的平移、缩放、旋转等变化。
第一个例子,绕坐标轴的旋转:
原始场景如图
使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6, 0, 0, 1); 绕Z轴旋转30度后的效果
使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6, 0, 1, 0); 绕Y轴旋转30度后的效果
使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6, 1, 0, 0); 绕X轴旋转30度后的效果
能够发现,绕Z轴的旋转比较符合预期,但是绕X轴Y轴的旋转只是在Y轴X轴上进行了一些缩放而已。这是因为,在CALayer的显示系统中,默认的相机使用正交投影,正交投影没有远小近大效果,所以在本例中,只能造成相应轴上的缩放。
第二个例子,透视投影
CALayer默认使用正交投影,因此没有远小近大效果,而且没有明确的API可以使用透视投影矩阵。所幸可以通过矩阵连乘自己构造透视投影矩阵。构造透视投影矩阵的代码如下:
CATransform3D CATransform3DMakePerspective(CGPoint center, float disZ)
{
CATransform3D transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, 0);
CATransform3D transBack = CATransform3DMakeTranslation(center.x, center.y, 0);
CATransform3D scale = CATransform3DIdentity;
scale.m34 = -1.0f/disZ;
return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack);
}
CATransform3D CATransform3DPerspect(CATransform3D t, CGPoint center, float disZ)
{
return CATransform3DConcat(t, CATransform3DMakePerspective(center, disZ));
}
这个函数的实现原理要参考计算机图形学的3D变换部分,以后再做解释。现在只需要了解接口的含义,center指的是相机 的位置,相机的位置是相对于要进行变换的CALayer的来说的,原点是CALayer的anchorPoint在整个CALayer的位置,例如CALayer的大小是(100, 200), anchorPoint值为(0.5, 0.5),此时anchorPoint在整个CALayer中的位置就是(50, 100),正中心的位置。传入透视变换的相机位置为(0, 0),那么相机所在的位置相对于CALayer就是(50, 100)。如果希望相机在左上角,则需要传入(-50, -100)。disZ表示的是相机离z=0平面(也可以理解为屏幕)的距离。
带透视效果的旋转,效果如下:
CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 1, 0, 0);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);
CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 0, 1, 0);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);
CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 0, 0, 1);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);
image的默认anchorPoint为(0.5,0.5),也就是在图片中心;眼睛在图片中心点,距屏幕200单位。可以观察到,因为翻转,使图片的不同部分离屏幕距离不同,近大远小的效果使立体感大大提升。饶Z轴的旋转不因为透视产生变化,因为所有的点离屏幕距离相同,所以不会产生近大远小的透视感。
第三,更多的效果。CALayer的旋转和缩放是绕anchorPoint点的,改变anchorPoint的值,可以使Layer绕不同的点而不只是中心点旋转缩放。在构造透视投影矩阵的例子中就可以看到,CATransform3D可以使用CATransform3DConcat函数连接起来以构造更复杂的变换。通过这些方法,可以组合出更多的效果来。下面是个翻转的动画。使用UITimer
- (void)update
{
static float angle = 0;
angle += 0.05f;
CATransform3D transloate = CATransform3DMakeTranslation(0, 0, -200);
CATransform3D rotate = CATransform3DMakeRotation(angle, 0, 1, 0);
CATransform3D mat = CATransform3DConcat(rotate, transloate);
image.layer.transform = CATransform3DPerspect(mat, CGPointMake(0, 0), 500);
}
最后是两个更复杂的例子。第一个是使用四张同样大小的图片围成一个框,让这个框动画旋转。
CATransform3D move = CATransform3DMakeTranslation(0, 0, 160);
CATransform3D back = CATransform3DMakeTranslation(0, 0, -160);
CATransform3D rotate0 = CATransform3DMakeRotation(-angle, 0, 1, 0);
CATransform3D rotate1 = CATransform3DMakeRotation(M_PI_2-angle, 0, 1, 0);
CATransform3D rotate2 = CATransform3DMakeRotation(M_PI_2*2-angle, 0, 1, 0);
CATransform3D rotate3 = CATransform3DMakeRotation(M_PI_2*3-angle, 0, 1, 0);
CATransform3D mat0 = CATransform3DConcat(CATransform3DConcat(move, rotate0), back);
CATransform3D mat1 = CATransform3DConcat(CATransform3DConcat(move, rotate1), back);
CATransform3D mat2 = CATransform3DConcat(CATransform3DConcat(move, rotate2), back);
CATransform3D mat3 = CATransform3DConcat(CATransform3DConcat(move, rotate3), back);
image0.layer.transform = CATransform3DPerspect(mat0, CGPointZero, 500);
image1.layer.transform = CATransform3DPerspect(mat1, CGPointZero, 500);
image2.layer.transform = CATransform3DPerspect(mat2, CGPointZero, 500);
image3.layer.transform = CATransform3DPerspect(mat3, CGPointZero, 500);
上面的例子都使用UIImage的CALayer,但CALayer产生的动画可以应用在所有的UIView及子类上,下面是个普通界面的立体翻转效果。
float dis = 160 * 1.732f;
CATransform3D move = CATransform3DMakeTranslation(0, 0, dis);
CATransform3D back = CATransform3DMakeTranslation(0, 0, -dis);
CATransform3D rotate0 = CATransform3DMakeRotation(-angle, 0, 1, 0);
CATransform3D rotate1 = CATransform3DMakeRotation(-angle + M_PI/3.0f, 0, 1, 0);
CATransform3D mat0 = CATransform3DConcat(CATransform3DConcat(move, rotate0), back);
CATransform3D mat1 = CATransform3DConcat(CATransform3DConcat(move, rotate1), back);
view0.layer.transform = CATransform3DPerspect(mat0, CGPointZero, 500);
view1.layer.transform = CATransform3DPerspect(mat1, CGPointZero, 500);
【转】如何在IOS中使用3D UI - CALayer的透视投影的更多相关文章
- 如何在ios中集成微信登录功能
在ios中集成微信的登录功能有两种方法 1 用微信原生的api来做,这样做的好处就是轻量级,程序负重小,在Build Settings 中这样设置 然后设置 友盟的设置同上,但是要注意,加入你需要的所 ...
- IOS 3D UI --- CALayer的transform扩展
http://www.cocoachina.com/bbs/read.php?tid=117061 例子代码可以在 http://download.csdn.net/detail/worldmatri ...
- 如何在 iOS 中解决循环引用的问题
稍有常识的人都知道在 iOS 开发时,我们经常会遇到循环引用的问题,比如两个强指针相互引用,但是这种简单的情况作为稍有经验的开发者都会轻松地查找出来. 但是遇到下面这样的情况,如果只看其实现代码,也很 ...
- iOS中 UISearchController 搜索栏 UI技术分享
<p style="margin-top: 0px; margin-bottom: 0px; font-size: 20px; font-family: 'STHeiti Light' ...
- iOS中 数据持久化 UI高级_17
数据持久化的本质就是把数据由内写到本地(硬盘中),在iOS指将数据写到沙盒文件夹下: 沙盒机制:指的就是采用沙盒文件夹的形式管理应用程序的本地文件,而且沙盒文件夹的名字是随机分配的,采用十六进制方法命 ...
- 5月8日——iOS中的3D Touch效果
需要在manifest.json文件中进行配置 需要执行的js代码为: 最终操作效果为 本篇文章主要采用了HTML5+ 中的 launcher属性 具体可参照 http://www.html5plu ...
- 如何在iOS中使用libxml
本文转载至 http://blog.csdn.net/cloudhsu/article/details/8087628 1. 选择xcode工程设定 2. 选择target 3. 选择Summary ...
- 如何在 Vite 中使用 Element UI + Vue 3
在上篇文章<2021新年 Vue3.0 + Element UI 尝鲜小记>里,我们尝试使用了 Vue CLI 创建 Vue 3 + Element UI 的项目,而 Vue CLI 实际 ...
- iOS 中捕获程序崩溃日志
iOS 中捕获程序崩溃日志 (2014-04-22 17:35:59) 转载▼ iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下 ...
随机推荐
- apache 多域名配置
一直不明白apache多域名配置的问题,所以只能用不同的端口来配置,现在终于搞懂了一点 首先,开启apache的vhost模块 找到配置文件httpd.conf中的下面两行 #LoadModule v ...
- 《head first java 》读书笔记(三)
Updated 2014/04/03 --P518 Thread需要任务,任务是实现过Runnable的实例.Runnalbe这个接口只有一个方法.run()会是新线程所执行的第一项方法.要把Runn ...
- 【C++基础】 类中static private public protected
静态成员在一个类的所有实例间共享数据 “类属性”,是描述类的所有对象共同特征的一个数据项,对所有对象,它的值相同,static定义,为整个类所共有.相对于“实例属性” 如果static成员是私有类型, ...
- UVA 11174 Stand in a Line (组合+除法的求模)
题意:村子里有n个人,给出父亲和儿子的关系,有多少种方式可以把他们排成一列,使得没人会排在他父亲的前面 思路:设f[i]表示以i为根的子树有f[i]种排法,节点i的各个子树的根节点,即它的儿子为c1, ...
- SDUT1281Cup
http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=1281 题意 : 一个杯子,告诉你底面半径,顶端 ...
- Play Framework 发现并没有热启动的特殊情况
解决办法: 删掉 target目录下的两个文件夹: src_mananger 和 twirl -----或者删掉整个target文件夹. 因为play framework 运行的是 在target 文 ...
- Android中使用HTTP和HttpClient进行通信
/** * 使用HTTP的Get方式进行数据请求 */ protected void httpGet() { /** * 进行异步请求 */ new AsyncTask<String, Void ...
- 如何实现Windows Phone代码与Unity相互通信(直接调用)
我之前用了两篇文章写了WP与Unity相互通信.调用的办法,一个是事件,一个是插件. 这次来说个更简单的,我觉得这应该是Unity发布到WP或者Win Store上得天独厚的优势.毕竟都是C#. 懒得 ...
- Unity打包APK横屏时的注意事项
由于你在Unity设置了横屏. 所以也需要在安卓的AndroidManifest.xml文件中, application/activity下声明为横屏.否则会黑屏,根本不给你报错,愁死你. 加上这一句 ...
- Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步
Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步 一.概述 PV操作是对信号量进行的操作. 进程同步是指在并发进程之间存在一种制约关系,一个进程的执行依赖另一个进程的消 ...