2012年12月11日 星期二

Core Animation的基本使用(十二)

CATransform3D数据结构定义了一个同质的三维变换(一个4x4的CGFloat矩阵),它被用来对图层进行旋转,缩放,位移,倾斜以及应用透视变换(3D)

其實使用上似乎與keyValue的轉換差不多,參考我的Core Animation的基本使用(六),因此除非有需要記錄轉換的位置及效果,否則應該不會用這個方式來做轉換。

层有两个属性用于指定变换矩阵:transform和sublayerTransform。transform属性指定的矩阵是相对于层的anchorPoint并应用到层和其子层上的。sublayerTransform指定的矩阵应用到层的子层上,而不应用于层自身。所有動作皆會參考到anchorPoint,此處請參考前一篇。

可以用以下几种方式创建和修改CATransform3D数据结构:
1.使用CATransform3D函数
2.直接编辑数据结构成员
3.使用键值编码(Key-Value Coding)和键路径

CATransform3DIdentity常量是单位矩阵,即没有应用缩放,旋转,倾斜或透视的矩阵。对层应用单位矩阵导致它显式默认的几何特性。


CATransform3D结构成员的意义。
?
structCATransform3D { m11(x缩放), m12(y切变), m13(旋转), m14(); m21(x切变), m22(y缩放), m23(), m24(); m31(旋转), m32(), m33(), m34() m41(x平移), m42(y平移), m43(z平移), m44(); };
PS1: m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义); 代表透视点离当前平面的距离,取值由-1~1各个,其中:
-1 表示透视点在负无穷,1 表示透视点在正无穷,0 表示透视点在当前平面。
PS2:每一個變數都是CGFloat  
PS3:整体比例变换时,也就是m11==m22时,若m33>1,图形整体缩小,若0<m33<1,图形整体放大,若s<0,发生关于原点的对称等比变换。


一般情况下我们不必对IOS的这个矩阵进行直接操作,SDK为我们提供了现成的转换方法。

貼上一個有趣的說明網址,有興趣的可以去看看。
http://www.zhangxinxu.com/wordpress/?p=2592


官方資料
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/CoreAnimation_functions/Reference/reference.html#//apple_ref/c/func/CATransform3DMakeTranslation


主要會用到下面幾個來作轉換。前三個可以從前一個轉換所產生的矩陣,連續下去做轉換。





官方資料的翻譯

表1 CATransform3D变换函数,用于移动,旋转和缩放

函数及用途

CATransform3DMakeTranslation
返回移动'(tx, ty, tx)'的变换。 t' = [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1].

CATransform3DTranslate
将't' 平移 '(tx, ty, tz)' 并返回结果: * t' = translate(tx, ty, tz) * t.

CATransform3DMakeScale
返回缩放'(sx, sy, sx)'的变换。: * t' = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1].

CATransform3DScale
将 't' 缩放 '(sx, sy, sz)' 并返回结果: * t' = scale(sx, sy, sz) * t.

CATransform3DMakeRotation
返回以向量'(x,y,z)'弧度的'角'旋转的变换。如果向量长度为0返回单位矩阵。

CATransform3DRotate
将't' 以向量'(x,y,z)'弧度的'角'旋转并返回结果. t' = rotation(angle, x, y, z) * t.


旋转的角度以弧度而不是角度为单位。下面的函数允许你在弧度和角度之间转换。

CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};

CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180 / M_PI;};


core animation提供了一个反转矩阵的变换函数,CATransform3DInvert。反转一般用于对被变换对象中的点提供反向变换。反转用于需要从已经进行矩阵变换后恢复一个值:反转这个矩阵,乘上这个反转矩阵的值,结果得到了原来的值。

也有些函数用来将CATransfrom矩阵转换为CGAffineTransfrom矩阵,如果CATransfrom矩阵可以那样表示的话。


表2 CATransfrom变换函数,用于CGAffineTransfrom转换

函数及用途

CATransform3DMakeAffineTransform
返回一个同传入的仿射矩阵效果相同的CATransfrom3D

CATransform3DIsAffine
如果CATransfrom3D可以确切的代表一个仿射矩阵的话,返回YES.

CATransform3DGetAffineTransform
返回传入的CATransform3D代表的仿射矩阵。

还有些函数提供比较变换矩阵是否和单位矩阵或另一个矩阵相同的功能。


表3 CATransform3D变换函数,用于检测相等性

函数及用途

CATransform3DIsIdentity
如果变换是单位变换,返回YES。

CATransform3DEqualToTransform
如果两个变换完全相等,返回YES。


编辑变换的数据结构(Modifying the Transform Data Structure)

你可以编辑CATransform3D数据结构的任何成员的值,列表1包含了CATransform3D结构的定义,结构成员位于相应的矩阵位置上。


以下是測試用的實作

1. 開啓一個新的專案

  

2. 在storyboard加入兩個SegmentControl,一個Button,一個textView,一個Switch作為輸入控制。並加入一個圖檔。

 



 3. 在mainViewController.h加入基本設定及所需變數。另外並需要加上一個QuatzCore的Framework在專案設定上。

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface mainViewController : UIViewController
{
    CALayer *transform3DLayer;
}

@property (weak, nonatomic) IBOutlet UIButton *animationButton;
@property (weak, nonatomic) IBOutlet UISegmentedControl *animationModeSwitch;
@property (weak, nonatomic) IBOutlet UISegmentedControl *animationDirection;
@property (weak, nonatomic) IBOutlet UITextField *valueInputField;
@property (weak, nonatomic) IBOutlet UISwitch *animationSaveSwitch;

@property (nonatomic) CATransform3D perspective;

@end
 

4. 在mainViewController.m加上控制程式碼

#import "mainViewController.h"

@interface mainViewController ()

@end

#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)
@implementation mainViewController

@synthesize animationButton;
@synthesize animationDirection;
@synthesize animationModeSwitch;
@synthesize valueInputField;
@synthesize animationSaveSwitch;

@synthesize perspective;

float inputvalue;


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    transform3DLayer = [CALayer layer];
    transform3DLayer.bounds = CGRectMake(0, 0, 250, 250);
    transform3DLayer.position = CGPointMake(200, 500);
    transform3DLayer.contents = (id)[UIImage imageNamed:@"TaiwanMap.jpg"].CGImage;
   
    perspective = CATransform3DIdentity;
   
    transform3DLayer.anchorPoint = CGPointMake(0.0, 0.0);
   
    [self.view.layer addSublayer:transform3DLayer];
   

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)startAnimation:(id)sender {
   
    inputvalue = [valueInputField.text floatValue];
   
    switch (animationModeSwitch.selectedSegmentIndex) {
        case 0:
            [self  setTranslateDirection];
            break;
           
        case 1:
            [self  setRotateDirection];
            break;
           
        case 2:
            [self  setScaleDirection];
            break;
           
           
        default:
            [self  setTranslateDirection];
            break;
    }

   
}
- (IBAction)animationModeChanged:(id)sender {
   
    switch ([sender selectedSegmentIndex]) {
        case 0:
            valueInputField.text = @"100";
            break;
           
        case 1:
            valueInputField.text = @"180";
            break;
           
        case 2:
            valueInputField.text = @"1.0";
            break;
           
        default:
            break;
       
    }
   
}
- (IBAction)animationDirectionChanged:(id)sender {
}

- (void) setTranslateDirection
{
    switch (animationDirection.selectedSegmentIndex) {
        case 0: // X
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DTranslate(perspective, inputvalue, 0,0);
                perspective = CATransform3DTranslate(perspective, inputvalue, 0,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeTranslation(inputvalue, 0,0);
            break;
           
        case 1: // Y
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DTranslate(perspective, 0, inputvalue ,0);
                perspective = CATransform3DTranslate(perspective, 0, inputvalue,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeTranslation( 0, inputvalue,0);
            break;
           
        case 2: // Z
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DTranslate(perspective, 0, 0,inputvalue);
                perspective = CATransform3DTranslate(perspective, 0, 0,inputvalue);
            }
            else
                transform3DLayer.transform = CATransform3DMakeTranslation( 0, 0,inputvalue);
            break;
           
           
        default:
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DTranslate(perspective, inputvalue, 0,0);
                perspective = CATransform3DTranslate(perspective, inputvalue, 0,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeTranslation(inputvalue, 0,0);
           
            break;
    }
}

- (void) setRotateDirection
{
    switch (animationDirection.selectedSegmentIndex) {
        case 0: // X
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue),1, 0,0);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue),1, 0,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(inputvalue),1, 0,0);
            break;
           
        case 1: // Y
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue), 0,1,0);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue), 0,1,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(inputvalue), 0,1,0);
            break;
           
        case 2: // Z
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue), 0,0,1);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue), 0,0,1);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(inputvalue), 0,0,1);
            break;
           
           
        default:
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue),1, 0,0);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(inputvalue),1, 0,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(inputvalue),1, 0,0);
           
            break;
    }
}

- (void) setScaleDirection
{
    switch (animationDirection.selectedSegmentIndex) {
        case 0: // X
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DScale(perspective, inputvalue, 1.0, 1.0);
                perspective = CATransform3DScale(perspective, inputvalue, 1.0, 1.0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeScale(inputvalue, 1.0, 1.0);
            break;
           
        case 1: // Y
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DScale(perspective, 1.0, inputvalue, 1.0);
                perspective = CATransform3DScale(perspective, 1.0,inputvalue, 1.0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeScale(1.0, inputvalue, 1.0);
            break;
           
        case 2: // Z
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DScale(perspective, 1.0, 1.0,inputvalue);
                perspective = CATransform3DScale(perspective, 1.0, 1.0, inputvalue);
            }
            else
                transform3DLayer.transform = CATransform3DMakeScale(1.0, 1.0,inputvalue);
            break;
           
           
        default:
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DScale(perspective, inputvalue, 1.0, 1.0);
                perspective = CATransform3DScale(perspective, inputvalue, 1.0, 1.0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeScale(inputvalue, 1.0, 1.0);
           
            break;
    }
}


@end
 

5. 執行結果,連續的各種轉換截圖。





 

 

4 則留言:

匿名 提到...

轉動和翻轉沒動作

平移ok的

kirenenko.tw 提到...

請參考結果圖,功能完全正常。請再次確認。

匿名 提到...

原來是跟我自己寫的程式碼互相衝到

請問那如果要改變轉動的基準點
如:原本是以右上為基準點 再轉

現在想改成以中心點或左上 為基準點轉動
那要用什麼方式更改

kirenenko.tw 提到...

請參考前一篇Core Animation的基本使用(十ㄧ),有詳細的說明。