2012年12月15日 星期六

Core Animation的基本使用(十六)

layer tree






addSublayer:
将层追加到接受者的子层数组中。

insertSublayer:atIndex:
将层插入到接收者指定的索引位置作为子层。

insertSublayer:below:
将层插入到接受者的子层数组中,位于指定子层的下面。

insertSublayer:above:
将层插入到接受者的子层数组中,位于指定子层的上面。

removeFromSuperlayer
将接收者从子层数组中或者接收者超层的mask属性中移出。

replaceSublayer:with:
用新层替换接收者的子层数组中的层。


你也可以用层的数组设置子层,只要设置超层的sublayers属性。当用层对象的数组设置sublayer属性时,必须确保这些层的superlayer被置为nil。

默认情况下,从一个可见的层树中插入或移出层会出发动画。当层被作为子层添加,父层返回标识为kCAOrderIn动作的动画被触发。当从层的子层中移除一 个层时,父层返回kCAOrderOut动作标识的动画被触发。替换子层中的一个层导致父层返回kCATransition动作标识的动画被触发。你可以 在操作层树时关掉动画,或者用任何动作标识改变动画。

当你使用addSublayer来添加一个子层的是,在把这个子层添加到父层层结构的最上层。所以它将会显示在任何现有层的上面。使用 insertSublayer方法可以再层结构中插入层。

要在一个特殊的索引里面插入层,可以使用atIndex 参数。

[ rootLayer insertSublayer: layer1 atIndex: 1 ];

要在另一个层的上面或者下面插入层,可以使用above 或者 below 参数

[
rootLayer insertSublayer: layer1 below:layer2 ];
[
rootLayer insertSublayer: layer1 above:layer2 ];

要把一个层从父层中删除,可以调用此层的removeFromSuperlayer来删除。

[ layer1 removeFromSuperlayer ];

要用一个不同的层来替换一个已存在的层,可以使用replaceSublayer方法。

[
rootLayer replaceSublayer: layer2 with: layer3 ];






要把一个层留在父层的层结构中,但是不显示它,可以使用层的hidden属性。你可以使用下面的代码来隐藏HUDView显示的内容,而不用去删除它。

- (void) ToggleHUD {
 

   layer1.hidden = (layer2.hidden == NO) ? YES : NO;
}

当rootLayer物体在屏幕上显示时,所有所有sublayer层将合并渲染。每类负责自己的层,但是当rootLayer被画到屏幕上的时候,所有layer将合并在一起。

该rootLayer不是可以唯一添加的层。子层可以有自己的子层,这样整个有层次的体系才能被建立。例如,你可以向layer1增加一个层,来显示 下層的组件。例如一个团队的标志。


对层重新定位和调整大小

在层被创建后,你可以移动和调整他的大小,只需简单的改变层的几何属性:frame,bounds,position或者zPosition。
如果一个层的needsDisplayOnBoundsChange属性为YES,当层的bounds改变时层的内容将会被重新缓存。默认情况下,needsDisplayOnBoundsChange属性是NO。

默认情况下,设置frame,bounds,position,anchorPoint和zPosition属性,会让层以动画方式改变到新值。

example:
// Create the tach's background layer
    tachLayer = [CALayer layer];
    tachLayer.bounds = CGRectMake(0, 0, 250, 250);
    tachLayer.position = CGPointMake(200, 500);
    tachLayer.contents = (id)[UIImage imageNamed:@"speed.png"].CGImage;
       
    CALayer *L1 = [CALayer layer];
    L1.frame = CGRectMake(0, 0, 100, 100);
    UIColor *c1 = [UIColor redColor];
    L1.name = @"L1";
    [L1 setBackgroundColor:(c1.CGColor)]; //
因为layer setBackgroundColor用到的是CGColor所以要进行一次转换
   
    CALayer *L2 = [CALayer layer];
    L2.frame = CGRectMake(50, 50, 100, 100);
    UIColor *c2 = [UIColor blueColor];
    L2.name = @"L2";
    [L2 setBackgroundColor:(c2.CGColor)];
   
   
    CALayer *L3 = [CALayer layer];
    L3.frame = CGRectMake(50, 0, 100, 100);
    UIColor *c3 = [UIColor greenColor];
    L3.name = @"L3";
    [L3 setBackgroundColor:(c3.CGColor)];
   
    CALayer *L4 = [CALayer layer];
    L4.frame = CGRectMake(0, 50, 100, 100);
    UIColor *c4 = [UIColor grayColor];
    L4.name = @"L4";
    [L4 setBackgroundColor:(c4.CGColor)];

     [self.view.layer addSublayer:tachLayer];

 
 //以上是基本的Layer設定,下面是操作subLayer的例子


   [ tachLayer addSublayer: L2];
   
    [ tachLayer addSublayer: L1];
   
    [ tachLayer insertSublayer: L2 atIndex: 1 ];
   
    [ tachLayer insertSublayer: L1 atIndex: 2 ];
    [ tachLayer insertSublayer: L2 atIndex: 3 ];
    [ tachLayer insertSublayer: L3 atIndex: 1 ];
    [ tachLayer insertSublayer: L4 atIndex: 0 ];
  
    [ tachLayer addSublayer: L2];
    

執行到此的效果

   
    L2.name = @"L2A";
    [ tachLayer addSublayer: L2];  
   
    [ tachLayer insertSublayer: L3 atIndex: 0.7 ];
   
    [ tachLayer insertSublayer: L2 below:L1];
  
    [ tachLayer insertSublayer: L3 above:L1];
   
    [ tachLayer insertSublayer: L4 above:L1];
    

到此的結果

   
    [ L1 removeFromSuperlayer ];
   
    [ tachLayer replaceSublayer: L3 with:L4 ];

    [ tachLayer replaceSublayer: L2 with:L1 ];
   
    L4.hidden = YES;

    L1.zPosition = 5;
   
    for (CALayer *layer in tachLayer.sublayers) {
    //for (int i=0;i<[tachLayer.sublayers count];i++)
        //[layer removeFromSuperlayer];
        NSLog(@"subLayer is =>%@",layer.name);
    }

 



實際剩下的subLayer
2012-12-13 00:33:00.733 myTest16[66113:c07] subLayer is =>L1
2012-12-13 00:33:00.734 myTest16[66113:c07] subLayer is =>L4




2012年12月14日 星期五

Core Animation的基本使用(十五)

CATransform3D的矩陣運算提供了切變轉換的功能。CATransform3D矩陣的資料可參考Core Animation的基本使用(十二)。我們只改變m21(X方向切變)及m12的值(Y方向切變)。變化值從-1~ 1。

切變在App上的實用性還不確定,僅作實驗瞭解其性質。


1. 實作沿用前一個專案,在storyboard上入兩個slide及Label,另外再加上一個button來啓動轉換。

2. 在mainViewController.h加上所需元件註冊。
@property (weak, nonatomic) IBOutlet UISlider *xDistortValueSlide;
@property (weak, nonatomic) IBOutlet UISlider *yDistortValueSlide;
@property (weak, nonatomic) IBOutlet UILabel *xDistortValueLabel;
@property (weak, nonatomic) IBOutlet UILabel *yDistortValueLabel;


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

@synthesize xDistortValueLabel;
@synthesize xDistortValueSlide;
@synthesize yDistortValueLabel;
@synthesize yDistortValueSlide;

//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(350, 350);
    transform3DLayer.contents = (id)[UIImage imageNamed:@"TaiwanMap.jpg"].CGImage;
   
    perspective = CATransform3DIdentity;
   
    transform3DLayer.anchorPoint = CGPointMake(0.0, 1.0);
   
    [self.view.layer addSublayer:transform3DLayer];
   
   
    animationValueSlider.maximumValue =200;
    animationValueSlider.minimumValue =-200;
    animationValueSlider.value = 100;
    animationValueLabel.text = [NSString stringWithFormat:@"移動%2.2f點",animationValueSlider.value];
   
    zDistanceValue.value = 200;
   
    zDistanceLabel.text = [NSString stringWithFormat:@"Z-Dis=%2.2f",zDistanceValue.value];
   
    xDistortValueSlide.value = 0;
    yDistortValueSlide.value = 0;
   
    xDistortValueLabel.text = [NSString stringWithFormat:@"X切變量 = %2.2f", xDistortValueSlide.value];
    yDistortValueLabel.text = [NSString stringWithFormat:@"Y切變量 = %2.2f", yDistortValueSlide.value];


   
}





- (IBAction)distortTransform:(id)sender {
   
    //transform3DLayer.transform = CGAffineTransformMakeDistort(0.3, 0.3);
   
    perspective = CATransform3DIdentity;
   
   // transform3DLayer.transform = CATransform3DConcat(perspective, CGAffineTransformMake(1, 0.3, 0.3, 1, 0, 0));

    perspective.m21 = xDistortValueSlide.value;  // X distort
   
    perspective.m12 = yDistortValueSlide.value;  // Y distort
   
    CATransform3D scale = CATransform3DIdentity;
   
    transform3DLayer.transform = CATransform3DConcat(perspective, scale);
   
}
- (IBAction)xDistortValueChange:(id)sender {
   
    xDistortValueLabel.text = [NSString stringWithFormat:@"X切變量 = %2.2f", xDistortValueSlide.value];
}
- (IBAction)yDistortValueChange:(id)sender {
   
    yDistortValueLabel.text = [NSString stringWithFormat:@"Y切變量 = %2.2f", yDistortValueSlide.value];

}



4. 執行結果







2012年12月13日 星期四

Core Animation的基本使用(十四)

CATransform3D 设置透视效果

在一般設定下CATransform3D的Rotation都是平面的,例如
CATransform3DRotate(origin, DegreesToRadians(180), 0.0f, 1.0f, 0.0f);  

如果需要透视效果(就是近大远小),现在只需要一个简单的改动即可实现此效果,在origin上修改其m34变量,如下所示:
CATransform3D origin = CATransform3DIdentity;
  1. CGFloat zDistance = -1.0 / 1500;  
  2. origin.m34 = zDistance;  
  3. CATransform3DRotate(origin, DegreesToRadians(180), 0.0f, 1.0f, 0.0f);  
 
其中,zDistance代表透视点离当前平面的距离,取值由-1~1各个,其中:
-1 表示透视点在负无穷,1 表示透视点在正无穷,0 表示透视点在当前平面。
通常情况下会取zDistance的值为负,负貌似表示透视点在当前平面的后面。
而且zDistance的取值不是线性分布的,取1及0.1的效果相差无几。

m34负责z轴方向的translation(移动),m34= -1/D,  默认值是0,也就是说D(=1500)相當於无穷大,这意味layer in projection plane(投射面)和layer in world coordinate重合了。

D越小透视效果越明显。

所谓的D,是eye(观察者)到投射面的距离。

matrix的定義需參考Core Animation的基本使用(十二)

實作中需要用到CATransform3DConcat

CATransform3DConcat
Concatenate 'b' to 'a' and return the result: t' = a * b.


實作如下


1. 沿用前一篇的專案,在storyboard上加上兩個Slider,兩個Label,一個Switch。然後刪除一個TextFiled,此處用Slider來取代,輸入值較方便。
 
 2. 在mainViewController.h加入相關的元件及所需要的變數
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface mainViewController : UIViewController
{
    CALayer *transform3DLayer;
    CALayer *mirrorLayer;
}

@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;
@property (weak, nonatomic) IBOutlet UISwitch *perspectiveSwitch;
@property (weak, nonatomic) IBOutlet UISlider *zDistanceValue;
@property (weak, nonatomic) IBOutlet UILabel *zDistanceLabel;
@property (weak, nonatomic) IBOutlet UISlider *animationValueSlider;
@property (weak, nonatomic) IBOutlet UILabel *animationValueLabel;


@end

3. 在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;

@synthesize perspectiveSwitch;
@synthesize zDistanceValue;
@synthesize zDistanceLabel;

@synthesize animationValueSlider;
@synthesize animationValueLabel;


//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(350, 350);
    transform3DLayer.contents = (id)[UIImage imageNamed:@"TaiwanMap.jpg"].CGImage;
   
    perspective = CATransform3DIdentity;
   
    transform3DLayer.anchorPoint = CGPointMake(0.0, 1.0);
   
    [self.view.layer addSublayer:transform3DLayer];
      
    animationValueSlider.maximumValue =200;
    animationValueSlider.minimumValue =-200;
    animationValueSlider.value = 100;
    animationValueLabel.text = [NSString stringWithFormat:@"移動%2.2f點",animationValueSlider.value];
   
    zDistanceValue.value = 200;
   
    zDistanceLabel.text = [NSString stringWithFormat:@"Z-Dis=%2.2f",zDistanceValue.value];
   

}
- (IBAction)mirrorEffection:(id)sender {
   
    mirrorLayer = [CALayer layer];
    mirrorLayer.bounds = transform3DLayer.bounds;
    mirrorLayer.position = transform3DLayer.position;
    mirrorLayer.contents = transform3DLayer.contents;
   
    mirrorLayer.anchorPoint = CGPointMake(0.0, 1.0);
   
    [self.view.layer addSublayer:mirrorLayer];
   
    //mirrorLayer.duration = 3.0; // 不可加,會讓transform沒有動作。
   
    mirrorLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(-180),1, 0,0);
   
    mirrorLayer.opacity = 0.3;
   
}

- (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";
            animationValueSlider.maximumValue =200;
            animationValueSlider.minimumValue =-200;
            animationValueSlider.value = 100;
            animationValueLabel.text = [NSString stringWithFormat:@"移動%2.2f點",animationValueSlider.value];
           
            break;
           
        case 1:
            //valueInputField.text = @"180";
           
            animationValueSlider.maximumValue =360;
            animationValueSlider.minimumValue =-360;
            animationValueSlider.value = 30;
            animationValueLabel.text = [NSString stringWithFormat:@"旋轉%2.2f度",animationValueSlider.value];
           
            break;
           
        case 2:
            //valueInputField.text = @"1.0";
           
            animationValueSlider.maximumValue =200;
            animationValueSlider.minimumValue =1;
            animationValueSlider.value = 100;
            animationValueLabel.text = [NSString stringWithFormat:@"縮放百分之%2.2f",animationValueSlider.value];
           

            break;
           
        default:
            break;
       
    }
   
}
- (IBAction)animationDirectionChanged:(id)sender {
}

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

- (void) setRotateDirection
{
    switch (animationDirection.selectedSegmentIndex) {
        case 0: // X
           
            if (perspectiveSwitch.on == 1)
            {
                perspective = CATransform3DMakePerspective(CGPointMake(0, 0), zDistanceValue.value);
               
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value),1, 0,0);
               
                break;
            }
           
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value),1, 0,0);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value),1, 0,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(animationValueSlider.value),1, 0,0);
            break;
           
        case 1: // Y
           
            if (perspectiveSwitch.on == 1)
            {
                perspective = CATransform3DMakePerspective(CGPointMake(0, 0), zDistanceValue.value);
               
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value), 0,1,0);
               
                break;
            }

           
           
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value), 0,1,0);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value), 0,1,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(animationValueSlider.value), 0,1,0);
            break;
           
        case 2: // Z
           
            if (perspectiveSwitch.on == 1)
            {
                perspective = CATransform3DMakePerspective(CGPointMake(0, 0), zDistanceValue.value);
               
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value), 0,0, 1);
               
                break;
            }
           
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value), 0,0,1);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value), 0,0,1);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(animationValueSlider.value), 0,0,1);
            break;
           
           
        default:
            if (animationSaveSwitch.on == 1) // ON
            {
                transform3DLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value),1, 0,0);
                perspective = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(animationValueSlider.value),1, 0,0);
            }
            else
                transform3DLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(animationValueSlider.value),1, 0,0);
           
            break;
    }
}

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


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

- (IBAction)zDisChange:(id)sender {
    zDistanceLabel.text = [NSString stringWithFormat:@"Z-Dis=%2.2f",zDistanceValue.value];
}
- (IBAction)animationValueChange:(id)sender {
   
    switch ([animationModeSwitch selectedSegmentIndex]) {
        case 0:

            animationValueLabel.text = [NSString stringWithFormat:@"移動%2.2f點",animationValueSlider.value];
           
            break;
           
        case 1:

            animationValueLabel.text = [NSString stringWithFormat:@"旋轉%2.2f度",animationValueSlider.value];
           
            break;
           
        case 2:
            animationValueLabel.text = [NSString stringWithFormat:@"縮放百分之%2.2f",animationValueSlider.value];
           
           
            break;
           
        default:
            break;
           
    }

}


@end

4. 執行結果









2012年12月12日 星期三

人生何以苦,緣在此劫中

完課後,歸途中,有感:

『人生何以苦,緣在此劫中,業果報不盡,妄斷煩惱根。』

Core Animation的基本使用(十三)

利用transform3D來實作一個倒影效果

產生一個重複的Layer,調整anchorPoint來確認翻轉的軸心,再使用Rotate X方向來做翻轉。
主要缺點,因為要調整anchorPoint,但此又會影響Layer顯示的位置,因此實際案例使用時要有副程式來調整anchorPointposition,以免影響了圖像的位置。

1. 使用前一篇的專案,加入一個Button,並移動一下圖檔的位置。

2. 在mainViewController.h加入一個mirror用的Layer。

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

@interface mainViewController : UIViewController
{
    CALayer *transform3DLayer;
    CALayer *mirrorLayer;
}

@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

3. 在mainViewController.m加上程式碼


- (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(350, 350);

    transform3DLayer.contents = (id)[UIImage imageNamed:@"TaiwanMap.jpg"].CGImage;
  
    perspective = CATransform3DIdentity;
  
    transform3DLayer.anchorPoint = CGPointMake(0.0, 1.0); //向下翻轉
    

    [self.view.layer addSublayer:transform3DLayer];
  
  
}
- (IBAction)mirrorEffection:(id)sender {
  
  
// 加上一個重疊的Layer,作為翻轉倒影之用,所以設定都要相同
    mirrorLayer = [CALayer layer];
    mirrorLayer.bounds = transform3DLayer.bounds;
    mirrorLayer.position = transform3DLayer.position;
    mirrorLayer.contents = transform3DLayer.contents;
  
    mirrorLayer.anchorPoint = CGPointMake(0.0, 1.0); 
//向下翻轉 兩個要一樣,這是
   
    [self.view.layer addSublayer:mirrorLayer]; 
  



    mirrorLayer.transform = CATransform3DRotate(perspective, DEGREES_TO_RADIANS(-180),1, 0,0);
  
    mirrorLayer.opacity = 0.3;
  
}



4. 執行結果,然後對主圖再翻轉一次後就會重疊,再翻一次就又回復了。



2012年12月11日 星期二

NSObject參考資料

class NSObject 是所有 Objective-C 類別的 Super-Class.
class NSObject 的命名方式, 名稱前方有 NS 代表的是 NeXTStep 作業系統,
這是一套由 NeXT.Inc 公司所開發的作業系統, 目前 NeXTStep 作業系統已經賣掉了,
也已經更名為 OpenStep 作業系統.

由於, Mac OS 的官方API, Cocoa API 前身就是 NeXTStep 的 API, 所以, 舊有的
Cocoa API 都是以 NS 為開頭, 也都是使用 Objective-C 寫成的, 目前這些舊有 class
也仍然保留 NS 為 class 的 Pre-fix 開頭.

其他資料可參考
http://rritw.com/a/caozuoxitong/OS/20111106/140088.html


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. 執行結果,連續的各種轉換截圖。