变 换
在这一章中,我们将要研究可以用来对图层旋转,摆放或者扭曲的CGAffineTransform,以及可以将扁平物体转换成三维空间对象的CATransform3D(而不是仅仅对圆角矩形添加下沉阴影)。
放 射 变 换
UIView的transform属性是一个CGAffineTransform类型,用于在二维空间做旋转,缩放和平移。
UIView可以通过设置transform属性做变换,但实际上它只是封装了内部图层的变换。CALayer同样也有一个transform属性,但它的类型是CATransform3D,而不CGAffineTransform。CALayer对应于UIView的transform属性叫做affineTransform。
下面是使用affineTransform对图层旋转45度:

混 合 变 换
Core Graphics提供了一系列的函数可以在一个变换的基础上做更深层次的变换,如果做一个既要缩放又要旋转的变换,这就会非常有用了。当操纵一个变换的时候,初始生成一个什么都不做的变换很重要--也就是创建一个CGAffineTransform类型的空值。
用这些函数组合一个更加复杂的变换,先缩小50%,再旋转30度,最后向右移动200个像素,使用若干方法创建一个复合变换.


图5.4中有些需要注意的地方:图片向右边发生了平移,但并没有指定距离那么远(200像素),另外它还有点向下发生了平移。原因在于当你按顺序做了变换,上“一个变换的结果将会影响之后的变换,所以200像素的向右平移同样也被旋转了30度,缩小了50%,所以它实际上是斜向移动了100像素。这意味着变换的顺序会影响最终的结果,也就是说旋转之后的平移和平移之后的旋转结果可能不同。
3D 变 换
我们来试着创建一个固态的3D对象(实际上是一个技术上所谓的空洞对象,但它以固态呈现)。我们用六个独立的视图来构建一个立方体的各个面。
我们把一个有颜色的UILabel放置在视图内部,是为了清楚的辨别它们之间的关系,并且UIButton被放置在第三个面视图里面,后面会做简单的解释。
清单5.9 创建一个立方体:


从这个角度看立方体并不是很明显;看起来只是一个方块,为了更好地欣赏它,我们将更换一个不同的视角。旋转这个立方体将会显得很笨重,因为我们要单独对每个面做旋转。另一个简单的方案是通过调整容器视图的sublayerTransform去旋转照相机。
添加如下几行去旋转containerView图层的perspective变换矩阵:

这就对相机(或者相对相机的整个场景,你也可以这么认为)绕Y轴旋转45度,并且绕X轴旋转45度。现在从另一个角度去观察立方体,就能看出它的真实面貌(图5.21)

光 亮 和 阴 影
现在它看起来更像是一个立方体没错了,但是对每个面之间的连接还是很难分辨。Core Animation可以用3D显示图层,但是它对光线并没有概念。如果想让立方体看起来更加真实,需要自己做一个阴影效果。如果需要动态地创建光线效果,你可以根据每个视图的方向应用不同的alpha值做出半透明的阴影图层,但为了计算阴影图层的不透明度,你需要得到每个面的正太向量(垂直于表面的向量),然后根据一个想象的光源计算出两个向量叉乘结果。叉乘代表了光源和图层之间的角度,从而决定了它有多大程度上的光亮。清单5.10实现了这样一个结果,我们用GLKit框架来做向量的计算(你需要引入GLKit库来运行代码),每个面的CATransform3D都被转换成GLKMatrix4,然后通过GLKMatrix4GetMatrix3函数得出一个3×3的旋转矩阵。这个旋转矩阵指定了图层的方向,然后可以用它来得到正太向量的值。试着调整LIGHT_DIRECTION和AMBIENT_LIGHT的值来切换光线效果。(CATtransform3D内存结构一致,但坐标类型有长度区别,所以理论应该做一次float到CGFloat的转换)



点 击 事 件
第三个表面顶部的按钮点击没有反应,为什么呢?这并不是因为iOS在场景下正确地处理响应事件,实际上是可以做到的。问题在于视图顺序。所以按照视图/图层顺序来说,4,5,6在3的前面。即使我们看不见4,5,6的表面(因为被1,2,3遮住了),iOS在事件响应上仍然保持之前的顺序。当试图点击表面3上的按钮,表面4,5,6截断了点击事件(取决于点击的位置),这就和普通的2D布局在按钮上覆盖物体一样。
这里有几种正确的方案:把除了表面3的其他视图userInteractionEnabled属性都设置成NO来禁止事件传递。或者简单通过代码把视图3覆盖在视图6上。无论怎样都可以点击按钮了。

总结: 在本章中,你看到了图层背后到底是如何呈现的,并且知道了不能把扁平的图片做成真实的立体效果,最后我们用demo说明了触摸事件的处理,视图中图层添加的层级顺序会比屏幕上显示的顺序更有意义。 接下来的这一章会研究一些Core Animation提供不同功能的具体的CALayer子类。
网友评论