热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【转】如何在IOS中使用3DUICALayer的透视投影

原文网址:http:www.tairan.comarchives2041例子代码可以在 http:www.tairan.comthread-3607-1-1.html 下载   i

原文网址: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.framework中包含了CALayer以及CALayer一些官方子类的定义。

    通过设置CALayer的transform属性,可以使CALayer产生3D空间内的平移、缩放、旋转等变化。

    第一个例子,绕坐标轴的旋转:

    原始场景如图

    【转】如何在IOS中使用3D UI - CALayer的透视投影

 

    使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6001); 绕Z轴旋转30度后的效果

    【转】如何在IOS中使用3D UI - CALayer的透视投影

    使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6, 0, 1, 0); 绕Y轴旋转30度后的效果

     【转】如何在IOS中使用3D UI - CALayer的透视投影

    使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6, 10, 0); 绕X轴旋转30度后的效果

    【转】如何在IOS中使用3D UI - CALayer的透视投影

    能够发现,绕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平面(也可以理解为屏幕)的距离。

    带透视效果的旋转,效果如下:

    【转】如何在IOS中使用3D UI - CALayer的透视投影

 

CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 1, 0, 0);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);

    

    【转】如何在IOS中使用3D UI - CALayer的透视投影

CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 0, 1, 0);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);

    

    【转】如何在IOS中使用3D UI - CALayer的透视投影

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

    【转】如何在IOS中使用3D UI - CALayer的透视投影

 

- (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);
}

    最后是两个更复杂的例子。第一个是使用四张同样大小的图片围成一个框,让这个框动画旋转。

    【转】如何在IOS中使用3D UI - CALayer的透视投影

    

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及子类上,下面是个普通界面的立体翻转效果。 

    【转】如何在IOS中使用3D UI - CALayer的透视投影

    

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);

推荐阅读
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
author-avatar
随便人呐
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有