iOS核心动画--3D显示

仿射变换

实际上UIView 的transform属性是一个CGAffineTransform类型,用于在二维空间做旋转,缩放和平
移。CGAffineTransform是一个可以和二维空间向量(例如 CGPoint )做乘法 的3X2的矩阵

image.png

用 CGPoint的每一列和 矩阵的每一行对应元素相乘再求和,就形成了一个新的 类型的结果。要解释一下图中显示的灰色元素,为了能让矩阵做乘法,左边矩阵的列数一定要和右边矩阵的行数个数相同,所以要给矩阵填充一些标志值,使得既可以让矩阵做乘法,又不改变运算结果,并且没必要存储这些添加的值,因为它们的值不会发生变化,但是要用来做运算。

当对图层应用变换矩阵,图层矩形内的每一个点都被相应地做变换,从而形成一个 新的四边形的形状。 CGAffineTransform中的“仿射”的意思是无论变换矩阵用什么值,图层中平行的两条线在变换之后任然保持平行, CGAffineTransform 可以做出任意符合上述标注的变换

image.png

Core Graphics提供了一系列函数,对完全没有数学基础的开发者也能够简单地做一些变换。如下几个函数都 创建了一个 CGAffineTransform 实例:

CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)

旋转和缩放变换都可以很好解释--分别旋转或者缩放一个向量的值。平移变换是指 每个点都移动了向量指定的x或者y值--所以如果向量代表了一个点,那它就平移了 这个点的距离。

UIView可以通过设置transform属性做变换,但实际上它只是封装了内部图层的变换。

CALayer同样也有一个transform的属性,但它的类型是 CATransform3D,而不是CGAffineTransform。CALayer对应于UIView的transform属性叫做affineTransform。

- (void)viewDidLoad
{
    [super viewDidLoad];
    //rotate the layer 45 degrees
    CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
    self.layerView.layer.affineTransform = transform;
}

注意我们使用的旋转常量是 M_PI_4 ,而不是你想象的45,因为iOS的变换函数使 用弧度而不是角度作为单位。弧度用数学常量pi的倍数表示,一个pi代表180度,所以四分之一的pi就是45度。

C的数学函数库(iOS会自动引入)提供了pi的一些简便的换算, M_PI_4于是就 是pi的四分之一,如果对换算不太清楚的话,可以用如下的宏做换算:

 #define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)

Core Graphics提供了一系列的函数可以在一个变换的基础上做更深层次的变换, 如果做一个既要缩放又要旋转的变换,这就会非常有用了。例如下面几个函数:

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

当操纵一个变换的时候,初始生成一个什么都不做的变换很重要--也就是创建一 个 CGAffineTransform类型的空值,矩阵论中称作单位矩阵,Core Graphics同 样也提供了一个方便的常量:CGAffineTransformIdentity

最后,如果需要混合两个已经存在的变换矩阵,就可以使用如下方法,在两个变换
的基础上创建一个新的变换:

 CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)

Demo
我们来用这些函数组合一个更加复杂的变换,先缩小50%,再旋转30度,最后向右移动200个像素


- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a new transform
    CGAffineTransform transform = CGAffineTransformIdentity;
    //scale by 50%
    transform = CGAffineTransformScale(transform, 0.5, 0.5);
    //rotate by 30 degrees
    transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30);
    //translate by 200 points
    transform = CGAffineTransformTranslate(transform, 200, 0);
    //apply transform to layer
    self.layerView.layer.affineTransform = transform;
}

CATransfrom3D --3D变换

CG的前缀告诉我们, CGAffineTransform类型属于Core Graphics框架,Core Graphics实际上是一个严格意义上的2D绘图API,并且 CGAffineTransform 仅仅 对2D变换有效。

我们提到了zPosition 属性,可以用来让图层靠近或者远离相机 (用户视角),transform属性( CATransfrom3D类型)可以真正做到这点,即让图层在3D空间内移动或者旋转。

CGAffineTransform类似, CATransform3D 也是一个矩阵,但是和2x3的矩 阵不同,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵。

image.png

CGAffineTransform矩阵类似,Core Animation提供了一系列的方法用来创建和组合CATransform3D类型的矩阵, 和Core Graphics的函数类似, 但是3D的平移和旋转多出了一个z参数,并且旋转函数除了angle之外多出了x,y,z三个参数,分别决定了每个坐标方向上的旋转:

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

你应该对X轴和Y轴比较熟悉了,分别以右和下为正方向,Z轴和这两个轴分别垂直,指向视角外为正方向。

Demo
使用代码利用CATransform3DMakeRotation对视图内的图层 绕Y轴做了45度角的旋转,我们可以把视图向右倾斜,这样会看得更清晰。

- (void)viewDidLoad
{
    [super viewDidLoad];
    //rotate the layer 45 degrees along the Y axis
    CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0,1,0);
    self.layerView.layer.transform = transform;
}

角度

    /*
     旋转的角度,后面三个是xyz,围绕哪个轴旋转就把哪个设置为1
     */
    CATransform3DMakeRotation(<#CGFloat angle#>, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>);

缩放

/*
 缩放,在没个轴上缩放多少,都给你给一个值
 */
//    CATransform3DMakeScale(<#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)

平移

/*
     平移,沿着xyz各平移多少
    */
//    CATransform3DMakeTranslation(<#CGFloat tx#>, <#CGFloat ty#>, <#CGFloat tz#>)

具体代码

/*这样设置对z旋转有效,因为旋转z轴后,图层依然是正对着用户的,而x、y轴则是需要立体才能看到。对x、y轴进行旋转,因为需要3d效果才能看到,只是下面这样设置是不行的,需要按照下下面方式设置成透视投影才能看出来*/
    CATransform3D transform3D = CATransform3DMakeRotation(M_PI_4, 1, 0, 0);
    self.imageView.layer.transform = transform3D;

    /*
     投影方式:1、正投影,2、透视投影
     */
    /*
     当我们针对平面图形进行旋转时候,我们要修改他的透视投影
     */
    //     CATransform3DIdentity是单元矩阵的意思
    CATransform3D transform3D1 = CATransform3DIdentity;
    //设置单元矩阵中m34的值,变成透视投影
    transform3D1.m34 = -1.0/500;
    /*
     矩阵
     弧度
     围绕哪个轴旋转就给哪个参数1
     */
    transform3D1 = CATransform3DRotate(transform3D1, M_PI, 0, 1, 0);
//    self.imageView.layer.transform = transform3D1;

    //设置多个子图层都做仿射变换
    self.subView.layer.sublayerTransform = transform3D1;

    //当我们旋转180度时候,也能展示照片,这是因为苹果默认是开启正背面渲染的,也就是正面背面都能看到,但是这样很耗性能,因为用户只能看到一个面,看不到那一面没必要渲染取消正面背面渲染的话
    self.imageView.layer.doubleSided = NO;//关闭正背面渲染

CATransform3D

当父图层向外转,子图层向里转的时候,展示需要3D效果,但是手机本身是平面的,无法展现出3D效果,这个时候需要专用3D图层来提供立体空间来展示

正方体的展示

- (void)viewDidLoad {
    [super viewDidLoad];

    self.containerView = [[UIView alloc]init];
    self.containerView.frame = CGRectMake(30, 100, 300, 300);
    self.containerView.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:self.containerView];

    self.faces = @[self.view1,self.view2,self.view3,self.view4,self.view5,self.view6];

    //父视图的layer图层
    CATransform3D perspective = CATransform3DIdentity;
    //设置为透视视图
    perspective.m34 = -1/500;
    //延x轴旋转45度
    perspective = CATransform3DRotate(perspective, M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, M_PI_4, 0, 1, 0);

    self.containerView.layer.sublayerTransform = perspective;

    CGFloat juli = 100;
    //添加第一个视图,让第一个视图沿z轴平移100
    CATransform3D transform1 = CATransform3DMakeTranslation(0, 0, juli);
    [self addFace:self.faces[0] transform:transform1];
    //添加第二个面,研x轴平移100,并且研Y轴旋转90度,使用make可以让transform更新,而不使用make的api会让效果叠加
    transform1 = CATransform3DMakeTranslation(juli, 0, 0);
    transform1 = CATransform3DRotate(transform1, M_PI_2, 0, 1, 0);
    [self addFace:self.faces[1] transform:transform1];

    //第三个面
    transform1 = CATransform3DMakeTranslation(0, -100, 0);
    transform1 = CATransform3DRotate(transform1, M_PI_2, 1, 0, 0);
    [self addFace:self.faces[2] transform:transform1];

    //第四个面
    transform1 = CATransform3DMakeTranslation(0, 100, 0);
    transform1 = CATransform3DRotate(transform1, -M_PI_2, 1, 0, 0);
    [self addFace:self.faces[3] transform:transform1];

    //第五个面
    transform1 = CATransform3DMakeTranslation(-100, 0,0);
    transform1 = CATransform3DRotate(transform1, -M_PI_2, 0, 1, 0);
    [self addFace:self.faces[4] transform:transform1];

    //第六个面
    transform1 = CATransform3DMakeTranslation(0, 0, -100);
    transform1 = CATransform3DRotate(transform1, M_PI, 0, 1, 0);
    [self addFace:self.faces[5] transform:transform1];

}



//将正方体的六个面都添加尽量,并做相应的旋转
-(void)addFace:(UIView *)view transform:(CATransform3D)transform{

    [self.containerView addSubview:view];

    CGSize containerSize = self.view.frame.size;
    view.center = CGPointMake(containerSize.width/2, containerSize.height/2);
    view.layer.transform = transform;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343