第三十九篇:IOS核心高階動畫 Core Animation、Core Graphics
一、 CALayer < CAMediaTiming > 圖層:
1. 圖層屬性及功能介紹:
1)contents :id 型別, 內容顯示。 在ARC環境下使用: = (__bridge id)image.CGImage; 在MRC環境下使用:去掉(__bridge id) 2)contentsGravity:字串型,圖層的內容模式。功能與UIView 的contentMode用法相同;比如內容居中顯示使用 kCAGravityCenter (值為字串)。 3)contentsScale:浮點型,內容縮放。每個點與畫素個數比例為 1:1。 4)contentsRect:NSRect型,在圖層邊框只顯示寄存圖的一個子域,猶如截圖 。CGRect型別:不是按點來計算,是按比例計算。 5)contentsCenter:CGRect型別,定義一個固定的邊框和一個可拉伸的區域。值是0~1(按比例) 6)poistion:CGPoint 型別,相對於父圖層,自身的中心點。 7)anchorPoint:CGPoint 型別,錨點。 錨點即指相對於自身而言的一個點,其範圍在0~1之間 或 < 0 或 >1,表示x和y方向分別相對於自身的寬高比例,總與position點重合,而position是相對於父控制位置的點。 當錨點被設定成相對於自身bounds的點後,就會平移對應的圖層,使錨點與中心點重合,就算移動了但中心點是不會變的。 8)zPosition:CGFloat 型別,3D z 軸方向的距離。 可以明顯改變螢幕上圖層的顯示順序,但不能改變事件的傳遞順序。 9)geometryFlipped:view.layer 的 geometryFlipped 屬性 對subView的佈局產生影響。設定YES,則subView.top 相對父view.bottom 了,也就形成了按水平中心線做了翻轉。
CAMediaTiming 動畫 協議 會在後面講到
2. 圖層方法有功能介紹:
例如下程式碼:1)- (void)renderInContext:(CGContextRef)ctx; 截圖 功能。如下: // 1.截圖 UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 0.0); [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; // 擷取的圖片 UIImage * currentImage = UIGraphicsGetImageFromCurrentImageContext(); // 在使用UIGraphicsBeginImageContextWithOptions函式一定要有對應的 // UIGraphicsEndImageContext函式作為結尾,不然會有記憶體洩漏 UIGraphicsEndImageContext() ;
#import "ViewController.h" @interface ViewController () @property (nonatomic , strong) UIButton * transitionBtn ; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor redColor]; self.transitionBtn = [UIButton buttonWithType:UIButtonTypeCustom]; self.transitionBtn.backgroundColor = [UIColor whiteColor]; [self.transitionBtn setTitle:@"perform transition" forState:UIControlStateNormal]; [self.transitionBtn addTarget:self action:@selector(performTransition) forControlEvents:UIControlEventTouchUpInside]; self.transitionBtn.bounds = CGRectMake(0, 0, 100, 50); self.transitionBtn.center = self.view.center ; [self.view addSubview:self.transitionBtn]; } -(void)performTransition { // 1.截圖 UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 0.0); [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; // 擷取的圖片 UIImage * currentImage = UIGraphicsGetImageFromCurrentImageContext(); // 在使用UIGraphicsBeginImageContextWithOptions函式一定要有對應的 // UIGraphicsEndImageContext函式作為結尾,不然會有記憶體洩漏 UIGraphicsEndImageContext() ; // 2.把擷取的圖新增到view上 UIImageView * currentImageView = [[UIImageView alloc] initWithImage:currentImage]; currentImageView.frame = self.view.bounds ; [self.view addSubview:currentImageView]; // 3.隨機改變view的背景色 CGFloat red = arc4random() / (CGFloat)INT_MAX ; CGFloat greed = arc4random() / (CGFloat)INT_MAX ; CGFloat blue = arc4random() / (CGFloat)INT_MAX ; self.view.backgroundColor = [UIColor colorWithRed:red green:greed blue:blue alpha:1.0]; // 4.設定動畫 [UIView animateWithDuration:1.5 animations:^{ CGAffineTransform transform = CGAffineTransformMakeScale(0.0001f, 0.0001f); transform = CGAffineTransformRotate(transform, M_PI_2) ; currentImageView.transform = transform ; currentImageView.alpha = 0.0 ; } completion:^(BOOL finished) { [currentImageView removeFromSuperview]; }]; }
2)-(BOOL)containsPoint:(CGPoint)point;
功能:當在螢幕點選了圖層的某個點point,如果要判斷具體點選哪個圖層,那麼就需要用該方法斷每個圖層是否包含point點。
3)-(CALayer*)hitTest:(CGPoint)point;
功能:直接返回一個currentLayer 的 bounds 所對應的point點所在的最上層的layer物件,即返回真實被點選的layer圖層。
4)- (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)L;
功能:把 L 圖層的bounds 所對應的 point 轉成 當前 currentLayer 圖層的 bounds 所對應的 point_1 位置;
5)- (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;
功能:把當前currentLayer圖層所對應的point轉成L圖層的bounds所對應的pint_1位置。
6)-(void)layoutSublayerOfLayer:(CALayer *)layer;
功能:當圖層的bounds發生改變或圖層的-setNeedsLayout方法被呼叫時,這個涵數將會被執行。但是不能像UIView的自動佈局autorssizingMask 和 constraints屬性做到自適應螢幕旋轉。這也是為什麼最好使用UIView而不單獨用圖層來構建應用程式的重要原因之一。
3. 邊框陰影介紹。
簡介:圖層的陰影繼承自內容的外形(也就是layer.contents 或subLayer) ,而不是根據邊界和角的半徑來確定。
1)系統是根據層圖的內容來設定陰影,而不是根據邊框來設定陰影。
@ 屬性設定(低效能)
1.1 : shadowOpacity :透明度 。
1.2 : shadowOffset :偏移量:水平和垂直。
1.3 :shadowRadius :陰影的半徑,越大顯示越自然。
1.4 :shadowColor :陰影顏色 。
1.5 :shadowPath :設定一個巨型邊框陰影。
2)例如:
2.1 如果layer.backgroundColor 是clearColor且layer有一張圖片,那麼陰影的設定是沿那張圖片的邊框來設定。
2.2 如果layer.backgroundColor 不是clearColor且layer有一張圖片,那麼陰影的設定是沿layer圖層的邊框來設定
3)效能高方法設定:設定圖層的 shadowPaht 屬性,這是給定了陰影顯示的邊框,不用系統去計算,直接顯示某形狀就OK了。
例如:
self.layer = [CALayer layer];
self.layer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.layer];
self.layer.shadowOpacity = 0.99 ;
// 邊框的陰
self.layer.shadowRadius = 20 ;
// 通過 layer.shadowPath 屬性來設定陰影範圍,效能高
CGMutablePathRef pathRef = CGPathCreateMutable();
// 矩形邊陰影
CGPathAddRect(pathRef, NULL, self.layer.bounds);
self.layer.shadowPath = pathRef ;
CGPathRelease(pathRef);
4. 圖層蒙板介紹。
簡介:設定圖層的蒙板即 設定 圖層的 mask 屬性,型別是CALayer,類似一個子圖層。
解釋:圖層蒙板的Color屬性無關緊要,真正重要的是圖層的輪廓。mask 就像一個餅乾切割機, mask 圖層實心部分會被保留下來,其他則會被拋棄。
例子:// 顯示的效果為:以image的形狀對subView進行剪下
self.subView = [[UIView alloc] init];
self.subView.backgroundColor = [UIColor redColor];
self.subView.frame = CGRectMake(0, 0, 300, 500);
[self.view addSubview:self.subView];
CALayer * maskLayer = [CALayer layer];
UIImage * image = [UIImage imageNamed:@"icon_share"] ;
maskLayer.frame = CGRectMake(150-image.size.width, 250-image.size.height, image.size.width, image.size.height) ;
maskLayer.contents = (__bridge id __nullable)image.CGImage;
maskLayer.contentsGravity = kCAGravityCenter ;
maskLayer.contentsScale = [UIScreen mainScreen].scale ;
self.subView.layer.mask = maskLayer ;
5. 拉伸過濾介紹。
簡介:相對的兩個屬性 ;一個是 minificationFilter 屬性,用於縮小功能;另一個是 magnificationFilter 屬性,用於放大功能。都是 NSString 型別,但都是列舉。設定不同的值有不同的效果,如下功能介紹 :
kCAFilterLinear(預設值):通過對多個畫素取樣最終生成新的值,得到一個平滑的表現不錯的拉伸。但是當放大倍數比較大時圖片就模糊不清。
kCAFilterNearest:是一種比較武斷的方法,就是取樣最近的單畫素點而不管其他的顏色,這樣做非常快,也不會使圖片模糊,但最明顯的效果是會使得壓縮圖片更糟,圖片放大後也顯得塊狀或是馬賽克嚴重。
kCAFilterTrilinear:與kCAFilterLinear非常相似,大部分情況下二者都看不出來,但比較而言,三線性濾波演算法儲存了多個大小情況下的圖片(也叫多重貼圖),並三維取樣,同時結合大圖和小圖的儲存進而得到最後的結果。效果顯示清楚。
6. 仿射變換
簡介:在旋轉時遵守一個規律,即:右手法則,大母指朝向原點,手握的方向即為旋轉的方向。屬性 layer.affineTransform 與 view.transform 功能一樣。layer.transform 為3D效果。
1)transform 3D效果
1.1:單獨變換的一些方法 :
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z); // 以點(x,y,z)為向量旋轉圖層angle度
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz); // 對x、y和z方向上的點分別進行sx、sy和sz倍的縮放
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz); // 對layer的x、y和z方向分別平移tx、ty和tz。
1.2: 混合變換的一些方法:
CATransform3DRotate (CATransform3D t,CGFloat angle, CGFloat x,CGFloat y, CGFloat z); // 在原有的變換基礎上以點(x,y,z)為向量旋轉圖層angle度
CATransform3DScale (CATransform3D t, CGFloat sx,CGFloat sy, CGFloat sz); // 在原有的基礎上對x、y和z方向上的點分別進行sx、sy和sz倍的縮放
CATransform3DTranslation(CATransform3D t,Gloat tx, CGFloat ty, CGFloat tz); // 在原有變換的基礎上對layer的x、y和z方向分別平移tx、ty和tz
CATransform3DConcat (CATransform3D a, CATransform3D b); // 在兩個變換的基礎上建立一個新的變換值
2)檢視透視投影,立體感
簡介:檢視投影,使得在視覺上會產生錯覺有立體感,結合上面的 3D 變換效果 會更好。2.1 修改m34值使透視投影有立體感:
2.1.1 、在現實生活中,當物體遠離我們的時候,由於視角的原因看起來會變小,理論上遠離我們的邊要比靠近視角的邊更短,但在實際上沒有這樣顯示 ,而我們當前的視角是等距離的,也就是在3D變換中保持平行,和之前提到的仿射類似變換類似。所以為了修改在視覺上的差義性,就需要設定 CATransform3D 的透視效果中和一個元素:m34。m34用於按比例縮放X和Y的值來計算到底要離檢視多遠。
2.1.2 、m34的值預設是0,我們可以通過設定 m34 為 -1.0 / D,D來應用透明效果,D代表想像中視角相機和螢幕之前的距離,以你素為單位。實現上我們大概估算就可以了,通常500~1000就已經很好了,D設定非常微的值會讓它基本失真,越大的值會讓它失去透明效果。( m34用於按比例縮放X和Y的值來計算到底要離檢視多遠 )。
2.2 3D變換後有立體感設定m34
2.2.1 : 單檢視投影立體感,如下
樣例:self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
self.imageView.frame = CGRectMake(30, 50, 300, 600);
[self.view addSubview:self.imageView];
self.imageView.layer.shadowOpacity = 0.5 ;
self.imageView.layer.shadowRadius = 4 ;
self.imageView.layer.shadowOffset = CGSizeMake(0, 0);
// 如果去掉transform3D.m34 = -1.0/500;程式碼,那麼沒有投影效果的立體感,只縮小了一點X和Y
CATransform3D transform3D = CATransform3DIdentity ;
transform3D.m34 = -1.0/500;
transform3D = CATransform3DRotate(transform3D, M_PI_4, 1, 0, 0);
self.imageView.layer.transform = transform3D ;
2.2.2 :設定多個子檢視投影立體感。只需設定 父檢視的 superView.layer.sublayerTransform 屬性,如下:
只需要設定 superView.layer.sublayerTransform 子圖層變換就會有立體感:
CATransform3D transform3D = CATransform3DIdentity ;
transform3D.m34 = -1.0/500;
self.view.layer.sublayerTransform = transform3D ;
優點:滅點被設定在容器圖層的中點,從而不需要再對子圖層分別設定了。這意味著你可以隨意使用posintion和frame來放置子圖層,而不需要把它們放置在螢幕的中心,然後為了保證統一的滅點用變換來做平移。
樣例: self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
self.imageView.frame = CGRectMake(50, 50, 100, 200);
[self.view addSubview:self.imageView];
self.imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
self.imageView2.frame = CGRectMake(150, 50, 100, 200);
[self.view addSubview:self.imageView2];
self.imageView.layer.shadowOpacity = 0.5 ;
self.imageView.layer.shadowRadius = 4 ;
self.imageView.layer.shadowOffset = CGSizeMake(0, 0);
self.imageView2.layer.shadowOpacity = 0.5 ;
self.imageView2.layer.shadowRadius = 4 ;
self.imageView2.layer.shadowOffset = CGSizeMake(0, 0);
// 如果去掉transform3D.m34 = -1.0/500;程式碼,那麼沒有投影效果的立體感,只縮小了一點X和Y
CATransform3D transform3D = CATransform3DIdentity ;
transform3D.m34 = -1.0/500;
self.view.layer.sublayerTransform = transform3D ;
self.imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
self.imageView2.layer.transform = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
2.3 doubleSided 屬性
表示:是否需要雙面顯示檢視的內容,即背面是否要描繪顯示出檢視的內容。
例如 layer 旋轉180 度 ,然後再設定 該屬性後 顯示效果。
2.3.1 : doubleSided = YES 在背面描繪顯示出檢視的內容,檢視旋轉了M_PI,所有的子檢視也旋轉了並可以從父檢視背面看到,因為系統在背面描繪了內容。浪費CPU。
例子:self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
self.imageView.frame = CGRectMake(50, 50, 100, 200);
[self.view addSubview:self.imageView];
self.imageView.layer.shadowOpacity = 0.5 ;
self.imageView.layer.shadowRadius = 4 ;
self.imageView.layer.shadowOffset = CGSizeMake(0, 0);
// 如果去掉transform3D.m34 = -1.0/500;程式碼,那麼沒有投影效果的立體感,只縮小了一點X和Y
// CATransform3D transform3D = CATransform3DIdentity ;
// transform3D.m34 = -1.0/500;
// self.view.layer.sublayerTransform = transform3D ;
self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
// self.imageView.layer.doubleSided = NO ;
2.3.2 : doubleSided = NO (預設值)不在背面描繪顯示出檢視的內容,檢視旋轉了M_PI,所有的子檢視也旋轉了,但不能從父檢視背面看到,因為系統不會描繪看原來看不到的內容。不浪費CPU。(背面一片空白的效果)
例子:self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"registerImageName"]];
self.imageView.frame = CGRectMake(50, 50, 100, 200);
[self.view addSubview:self.imageView];
self.imageView.layer.shadowOpacity = 0.5 ;
self.imageView.layer.shadowRadius = 4 ;
self.imageView.layer.shadowOffset = CGSizeMake(0, 0);
// 如果去掉transform3D.m34 = -1.0/500;程式碼,那麼沒有投影效果的立體感,只縮小了一點X和Y
// CATransform3D transform3D = CATransform3DIdentity ;
// transform3D.m34 = -1.0/500;
// self.view.layer.sublayerTransform = transform3D ;
self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
self.imageView.layer.doubleSided = NO ;
2.4 斜切檢視
例子: 檢視向x軸方面斜切,A那邊屬於向外 - (void)viewDidLoad {
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shearTransformImageName"]];
self.imageView.frame = CGRectMake(50, 50, 200, 400);
[self.view addSubview:self.imageView];
self.imageView.transform = CGAffineTransformShear(1, 0);
}
CGAffineTransform CGAffineTransformShear(CGFloat x , CGFloat y)
{
CGAffineTransform transform = CGAffineTransformIdentity ;
transform.c = -x ;
transform.b = y ;
return transform ;
}
7. CALayer 子類
1)CATransformLayer 變換:是一個專門用來建立三維檢視的一個layer,也可以說是多個layer的集合。它沒有多餘的API,可以這麼說,他只是承載了子 layer
例子:
-(CALayer *)faceWithTransform:(CATransform3D)transform
{
CALayer * layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, 100, 100);
CGFloat red = (rand()/(double)INT_MAX);
CGFloat green = (rand()/(double)INT_MAX);
CGFloat blue = (rand()/(double)INT_MAX);
layer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
layer.transform = transform ;
return layer ;
}
-(CATransformLayer *)cubeWithTransform:(CATransform3D)transf frame:(CGRect)frame
{
// create cube layer
CATransformLayer * cube = [CATransformLayer layer];
// add cube face 1 前
CATransform3D transform = CATransform3DMakeTranslation(0, 0, 50);
[cube addSublayer:[self faceWithTransform:transform]];
// add cube face 2 右
transform = CATransform3DMakeTranslation(50, 0, 0);
transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:transform]];
// add cube face 3 頂
transform = CATransform3DMakeTranslation(0,-50 , 0);
transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
[cube addSublayer:[self faceWithTransform:transform]];
// add cube face 4 底
transform = CATransform3DMakeTranslation(0, 50, 0);
transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
[cube addSublayer:[self faceWithTransform:transform]];
// add cube face 5 後
transform = CATransform3DMakeTranslation(0, 0, -50);
transform = CATransform3DRotate(transform, M_PI, 1, 0, 0);
[cube addSublayer:[self faceWithTransform:transform]];
// add cube face 6 左
transform = CATransform3DMakeTranslation(-50, 0, 0);
transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:transform]];
// set cube transform
cube.transform = transf ;
cube.frame = frame ;
cube.doubleSided = NO ;
return cube ;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 設定統一來點
CATransform3D sublayerTransform = CATransform3DIdentity ;
sublayerTransform.m34 = -1.0/500.0;
self.view.layer.sublayerTransform = sublayerTransform ;
// set up cube 1 add to superLayer and transform
CATransform3D transf1 = CATransform3DIdentity ;
transf1 = CATransform3DRotate(transf1, (M_PI_2/90.0)*3, 1, 0, 0);
transf1 = CATransform3DRotate(transf1, -(M_PI_2/90.0)*10, 0, 1, 0);
[self.view.layer addSublayer:[self cubeWithTransform:transf1 frame:CGRectMake(100, 100, 100, 100)]];
// set up cube 2 add to superLayer and transform
CATransform3D transf2 = CATransform3DIdentity ;
transf2 = CATransform3DRotate(transf2, -(M_PI_2/90.0)*30, 1, 0, 0);
transf2 = CATransform3DRotate(transf2, (M_PI_2/90.0)*30, 0, 1, 0);
[self.view.layer addSublayer:[self cubeWithTransform:transf2 frame:CGRectMake(100, 300, 100, 100)]];
}
2)CAShapeLayer畫圖
可以用來繪製所有能通過CGPath 來表示的形狀。這個圖形不一定要閉合,圖層的路徑也不一定不可破。可以控制 一些屬性比如:lineWith(線寬,用點表示單位),lineCap(線條結尾的樣子),和lineJoin(線條之前結合點的樣子);可以使用UIBezierPath幫助類建立了圖層的路徑,這樣就不用我們考慮人工釋放CGPath了。
例子:
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(175, 100)];
[path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
[path moveToPoint:CGPointMake(150, 125)];
[path addLineToPoint:CGPointMake(150, 175)];
[path addLineToPoint:CGPointMake(125, 225)];
[path moveToPoint:CGPointMake(150, 175)];
[path addLineToPoint:CGPointMake(175, 225)];
[path moveToPoint:CGPointMake(100, 150)];
[path addLineToPoint:CGPointMake(200, 150)];
CAShapeLayer * shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.lineWidth = 10 ;
shapeLayer.lineJoin = kCALineJoinRound;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.strokeColor = [UIColor blueColor].CGColor ;
shapeLayer.fillColor = [UIColor redColor].CGColor ;
shapeLayer.path = path.CGPath ;
[self.view.layer addSublayer:shapeLayer];
3)CATextLayer顯示文字
優點:CATextLayer要比UILabel渲染快得多。
例子:
self.textLayer = [CATextLayer layer];
self.textLayer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.textLayer];
// set text attributes
self.textLayer.foregroundColor = [UIColor redColor].CGColor ;
self.textLayer.alignmentMode = kCAAlignmentJustified ;
self.textLayer.wrapped = YES ;
UIFont * font = [UIFont systemFontOfSize:15];
CFStringRef fontName = (__bridge CFStringRef) font.fontName ;
self.textLayer.font = CGFontCreateWithFontName(fontName);
self.textLayer.fontSize = font.pointSize ;
NSString * text = @"dfdsafjioweofewfuofjoewhfw3efnkewhofewhweofhweoqfhewhfeowqhfoewfoewofnwwwfoewhfoewihfeoiw";
self.textLayer.string = text ;
// 不設定contentsScale在Retina螢幕下會畫素化,所以加上這個設定
self.textLayer.contentsScale = [UIScreen mainScreen].scale;
4)CAGradientLayer多顏色平滑漸變
特點:
CAGradientLayer 是用來生成兩種或更多顏色平滑漸變的,用 Core Graphics 複製一個 CAGradientLayer 並將內容繪製到一個普通圖層的寄宿圖也是有可能的,但 CAGradientLayer 的真正好處在於繪製使用了 ”硬體加速” 。也就是說效能快。
startPoint :顏色漸變渲染的起點,範圍[0~1.0],按倍數在自身取點;
endPoint :顏色漸變渲染的終點,範圍[0~1.0],按倍數在自身取點;
colors :漸變的顏色值,裡面是CGColorRef型別(並不是從NSObject派生而來),所以我們要用通過bridge轉換以確保編譯正常。
locations :預設情況下,這些顏色在空間上均勻地被染色,但是我們可以用 locations來調整空間。locations數組裡的內容屬性是一個用NSNumber包裝的浮點,範圍[0~1.0];這個浮點數定義了colors屬性中每個不同顏色的位置,locations陣列的大小必需和colors個數大小一樣,不然得到一片空白。
............
- (void)viewDidLoad {
[super viewDidLoad];
CAGradientLayer * gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:gradientLayer];
// set up colors
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor , (__bridge id)[UIColor blueColor].CGColor];
// set up start and end point [0~1.0]
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 1);
}
5)CAReplicatorLayer 複製子圖層
屬性:
//拷貝的次數
@property NSInteger instanceCount;
//是否開啟景深效果
@property BOOL preservesDepth;
//當CAReplicatorLayer的子Layer層進行動畫的時候,拷貝的副本執行動畫的延時
@property CFTimeInterval instanceDelay;
//拷貝副本的3D變換
@property CATransform3D instanceTransform;
//拷貝副本的顏色變換
@property(nullable) CGColorRef instanceColor;
//每個拷貝副本的顏色偏移引數
@property float instanceRedOffset;
@property float instanceGreenOffset;
@property float instanceBlueOffset;
//每個拷貝副本的透明度偏移引數
@property float instanceAlphaOffset;
//create a replicator layer and add it to our view
CAReplicatorLayer * replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.position = CGPointMake(self.view.center.x, self.view.center.y - 100);
replicatorLayer.bounds = CGRectMake(0, 0, 300, 300);
replicatorLayer.backgroundColor = [UIColor grayColor].CGColor;
[self.view.layer addSublayer:replicatorLayer];
// set up replice count
replicatorLayer.instanceCount = 10 ;
// set up instanceTransform
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DTranslate(transform, 0, 200, 0);
transform = CATransform3DRotate(transform, M_PI / 5.0 , 0, 0, 1);
transform = CATransform3DTranslate(transform, 0, -200, 0);
replicatorLayer.instanceTransform = transform ;
// 顏色變化
replicatorLayer.instanceBlueOffset = -0.1;
replicatorLayer.instanceGreenOffset = -0.1;
// 被複制的子圖層
CALayer * subLayer = [CALayer layer];
// x , y 值對圖形顯示有有影響
subLayer.frame = CGRectMake(100, 100, 100, 100);
subLayer.backgroundColor = [UIColor greenColor].CGColor;
[replicatorLayer addSublayer:subLayer];
6)CAScrollLayer 滑動圖層
CAScrollLayer與UISrollView和UITableView不同。CAScrollLayer可以無限滑動,但並不負責將觸控事件轉換為滑動事件,不渲染滾動條,也不實現任何IOS指定行為例如滑動反彈。
如果要實現滑動,那就必需新增一個UIPanGestureRecognizer手勢實現觸控事件響應。
提供了一些屬性和方法:
// 呼叫這個方法可以滑動到指定的位置,方法是從圖層樹中查詢並找到第一個可用的CAScrollLayer,然後滑動它使得指定點成為可視的。
- (void)scrollPoint:(CGPoint)p;
// 方法實現了同樣的事情只不過是作用在一個矩形上的。
- (void)scrollRectToVisible:(CGRect)r;
// 屬性決定圖層(如果存在的話)的哪一部分是當前的可視區域。
@property(readonly) CGRect visibleRect;
7)CATiledLayer 平鋪圖片
8)CAEmitterLayer建立實時例子動畫如:火,煙霧,雨等
屬性:
emitterCells :數組裡面型別用CAEmitterCell。
CAEMitterCell 一些屬性 :
color :指定了一個可以混合圖片內容顏色的混合。
emissionRange :屬性的範圍變化。屬性的值為2*M_PI,意味著例子可以從360度任意位置反射出來。如果指定一個小一些的值,可以創造出一個圓錐形。
alphaSpeed :指定值在時間線上的變化。值為-0.4時,就是說明例子的透明度每過一秒就是減少0.4,這樣就有發射出去之後逐漸小時的效果。
preservesDepth:是否將3D例子系統平面化到一個圖層(預設值)或者可以在3D空間中混合其他的圖層。
renderMode :控制著在視覺上粒子圖片是如何混合的。
值和效果:
kCAEmitterLayerAdditive:合併例子重疊部分的亮度使得看上去更亮。
例子:
- (void)viewDidLoad {
[super viewDidLoad];
//create particle emitter layer
CAEmitterLayer *emitter = [CAEmitterLayer layer];
emitter.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:emitter];
//configure emitter
emitter.renderMode = kCAEmitterLayerAdditive;
emitter.emitterPosition = CGPointMake(emitter.frame.size.width / 2.0, emitter.frame.size.height/2.0);
//create a particle template
CAEmitterCell *cell = [[CAEmitterCell alloc] init];
cell.contents = (__bridge id)[UIImage imageNamed:@"ic_message"].CGImage;
cell.birthRate = 150;
cell.lifetime = 5.0;
cell.color = [UIColor colorWithRed:1 green:0.5 blue:0.1 alpha:1.0].CGColor; cell.alphaSpeed = -0.4;
cell.velocity = 50;
cell.velocityRange = 50;
cell.emissionRange = M_PI * 2.0;
//add particle template to emitter
emitter.emitterCells = @[cell];
}
9) CAEAGLLayer: 用來顯示任意的OpenGL(高效能圖形繪製)圖形
OpenGL介紹:提供了Core Animation的基礎,它是底層的C介面,直接和iPhone 、iPad的硬體通訊,極少地抽象出來方法。OpenGL沒有物件或是圖層的概念。它只是簡單地處理三角形。
IOS5中,蘋果引入了一個新的框架叫做GLKit,它去掉了一些OpenGL的複雜特性,提供一個叫做CLKView的UIView子類,幫你處理大部分的設定和繪製工作。 前提是 各種各樣的OpenGL繪圖緩衝的底層可配置項仍然需要你們CAEGLLayer完成,用來顯示任總的OpenGL圖形。
10)AVPlayerLayer 播放視訊#import <AVFoundation/AVFoundation.h>
簡單使用:可以用+playerLayerWithPlayer: 方法建立一個已經綁定了視訊播放的圖層,或者可以先建立一個圖層,然後用player屬性繫結一個AVPlayer例項。
例子:可以播放一個音樂,但不能播放網路音樂
- (void)viewDidLoad {
[super viewDidLoad];
NSURL * url = [[NSBundle mainBundle] URLForResource:@"playerMuzice.mp3" withExtension:nil];
AVPlayer * player = [AVPlayer playerWithURL:url];
AVPlayerLayer * playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
playerLayer.frame = CGRectMake(0, 0, 100, 100);
playerLayer.position = self.view.center ;
[self.view.layer addSublayer:playerLayer];
[player play];
}
二、事務管理動畫
1. CATransaction管理事務可以修改屬性做動畫
1) 隱式動畫主要是作用於CALayer可動畫屬性上面,UIView對應的layer是不可以的,只要你改變屬性的值,它不是突兀的直接改變過去,而是一個有一個動畫的過程,這個時間等屬性你可以通過事務(CATransaction)來控制,如果你不自己提供一個事務,它的預設時間是0.25秒,當然這個可動畫屬性是需要觸發的,如果你一上來就設定一個值,可能看不到動畫效果.
事務實際上是Core Animation用來包含一系列屬性動畫集合的機制,任何用指定事務去改變可以做動畫的圖層屬性不會立馬發生變化,而是當事務一旦提交的時候開始用一個動畫過渡到新值。比如:置一個背景顏色。
事務是通過CATransaction來做管理,這個類的設計有些奇怪,不像你從它的命名預期的那樣去管理一個簡單的事務,而一疊你不能訪問的事務。CATransaction並沒有屬性或者例項方法,並且也不能用+alloc 和 -init 方法建立它。但可以用+begin 和 +commit 分別來入棧或者出棧。
任何可以做動畫的圖層屬性都會被新增到棧頂的事務,可以通過+setAnimationDuration:方法設定當前事務的動畫時間,或者通過+animationDuration方法來獲取值(預設0.25秒)
2)UIView對應的layer預設是不能做隱式動畫的,但只需要實現那個layer.delegate的- (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;方法返回一個CAAction字典。
還有一種就是通過CATransaction的例項,叫做推進過渡動畫
(
即平滑過渡,可以對圖層任何變化平滑過渡
)CATransition。
//add a custom action
CATransition *transition = [CATransition animation];
// 從邊沿的一測滑動進入,把舊的從另一側推出去
transition.type = kCATransitionPush;
// 從左側滑入
transition.subtype = kCATransitionFromLeft;
self.colorLayer.actions = @{@"backgroundColor": transition};
3)UIView有兩種方式來作動畫,但這兩種表面不同,實際上內部都是呼叫事務 CATransaction的 +begin和+commit方法做的:
1、+beginAnimations:context: 和 +commitAnimations
2、+animateWithDuration:animations:這樣可以避免開發者失誤造成的風險
4)
例如實現CALyaer背景色平滑變色(隱式動畫):
@interface ViewController ()
@property (nonatomic , strong)CALayer * bgColorLayer ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.bgColorLayer = [CALayer layer];
self.bgColorLayer.backgroundColor = [UIColor blueColor].CGColor;
self.bgColorLayer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.bgColorLayer];
UIButton * btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 210, 100, 50)];
[btn setTitle:@"隨機改變顏色" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(changeColor) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)changeColor
{
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
CGFloat red = (arc4random()%256)*1.0/255.0 ;
CGFloat green = (arc4random()%256)*1.0/255.0 ;
CGFloat blue = (arc4random()%256)*1.0/255.0 ;
self.bgColorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
[CATransaction commit];
}
5)完成塊
執行完指定的動畫後再執行完成動畫後的block,但注意完成動畫後需要執行的block程式碼位置要放在設定動畫程式碼的前面。如果位置放在最後,那麼效果會先執行block再執行動畫,因為事務就像一個棧,先進後出,即先進後執行。
例子先改變完顏色後執行block裡面的內容:
@interface ViewController ()
@property (nonatomic , strong)CALayer * bgColorLayer ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.bgColorLayer = [CALayer layer];
self.bgColorLayer.backgroundColor = [UIColor blueColor].CGColor;
self.bgColorLayer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.bgColorLayer];
UIButton * btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 210, 100, 50)];
[btn setTitle:@"隨機改變顏色" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(changeColor) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)changeColor
{
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
// 完成顏色改變後呼叫block,才會在改變顏色完成後正確呼叫block
[CATransaction setCompletionBlock:^{
CGAffineTransform transform = self.bgColorLayer.affineTransform ;
transform = CGAffineTransformRotate(transform, M_PI_4);
self.bgColorLayer.affineTransform = transform ;
}];
CGFloat red = (arc4random()%256)*1.0/255.0 ;
CGFloat green = (arc4random()%256)*1.0/255.0 ;
CGFloat blue = (arc4random()%256)*1.0/255.0 ;
self.bgColorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
// 完成後呼叫的block放在這個位置的話會先執行,然後再實現上面的動畫
// [CATransaction setCompletionBlock:^{
// CGAffineTransform transform = self.bgColorLayer.affineTransform ;
// transform = CGAffineTransformRotate(transform, M_PI_4);
// self.bgColorLayer.affineTransform = transform ;
//
// transform = self.bgColorLayer.affineTransform ;
// transform = CGAffineTransformRotate(transform, M_PI_4);
// self.bgColorLayer.affineTransform = transform ;
// }];
[CATransaction commit];
2. CAAnimation<CAMediaTiming>動畫
1)部分屬性:timingFunction 型別是CAMediaTimingFunction,設定動畫緩衝,預設為nil。
2)給layer圖層新增動畫
2.1、CAKeyframeAnimation 關鍵幀動畫
// 改變顏色
- (IBAction)changeColor
{
//create a keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"backgroundColor";
animation.duration = 2.0;
animation.values = @[
(__bridge id)[UIColor blueColor].CGColor,
(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor blueColor].CGColor ];
//apply animation to layer
[self.colorLayer addAnimation:animation forKey:nil];
}
2.1.2、CAKeyframeAnimation、CAShapeLayer 和 UIBezierPath用法建立動畫效果是那個三角紙飛機的position(中點)會沿著曲線滑動,持續時間是4s。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic , strong) UIView * contentView ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width ;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height ;
self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,screenWidth , screenHeight)];
self.contentView.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.contentView];
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 150)];
[bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(300.0*1/4, 0) controlPoint2:CGPointMake(300.0*3/4, 300)];
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
shapeLayer.path = bezierPath.CGPath ;
shapeLayer.lineWidth = 3.0 ;
// shapeLayer.backgroundColor = [UIColor grayColor].CGColor ;
shapeLayer.fillColor = [UIColor blueColor].CGColor ;
shapeLayer.strokeColor = [UIColor redColor].CGColor ;
[self.contentView.layer addSublayer:shapeLayer];
// 新增一個滑動的圖層
CALayer * slipLayer = [CALayer layer];
slipLayer.frame = CGRectMake(0, 0, 50, 50);
slipLayer.position = CGPointMake(0, 150);
slipLayer.contents = (__bridge id)[UIImage imageNamed:@"icon_tmpImageName"].CGImage ;
slipLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_4);
[self.contentView.layer addSublayer:slipLayer];
// 根據position建立一個(圖層的中心點)關建幀動畫,
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyframeAnimation.duration = 4.0 ;
keyframeAnimation.path = bezierPath.CGPath ;
keyframeAnimation.repeatCount = 100 ;
// 把rotationMode設定=kCAAnimationRotateAuto 根據曲線的切線自動旋轉
keyframeAnimation.rotationMode = kCAAnimationRotateAuto ;
// slipLayer 新增一個動畫
[slipLayer addAnimation:keyframeAnimation forKey:nil];
}
2.2 、CAPropertyAnimation屬性動畫
CAPropertyAnimation提供瞭如下方法來建立屬性動畫。
+ (instancetype)animationWithKeyPath:(NSString *)path;該方法僅需要一個引數,該引數只是一個字串的值,指定CALayer的動畫屬性名,該設定屬性動畫控制CALayer的哪個動畫屬性持續的改變。
除此之外,CAPropertyAnimation還支援如下屬性:
keyPath:該屬性值返回建立CAPropertyAnimation時指定的引數。
additive: 該屬性指定該屬性動畫是否以當前動畫效果為基礎。
cumulative:該屬性指定動畫是否為累加效果。
valueFunction: 該屬性值是一個CAValueFunction物件,該物件負責對屬性改變的插值計算,系統已經提供了預設的插值計算方式,因此一般無須指定該屬性。
2.3 、CABasicAnimation做旋轉的例子(使用虛擬屬性變換)
執行效果特別好,用transform.rotation而不是transform做動畫的好處如下:
(1).我們可以不通過關鍵幀一步旋轉多於180度的動畫。
(2).可以用相對值而不是絕對值(設定byValue而不是toValue)。
(3).可以不用建立CATransform3D,而是使用一個簡單的數值來指定角度。
(4).不會和transform.position或者transform.scale衝突(同樣是使用關鍵路徑來做獨立的動畫屬性)。
transform.rotation 屬性有一個奇怪的問題是它其實並不存在。這是因為 CATransform3D 並不是一個物件,它實際是一個結構體,也沒有符合 KVC 相關的屬性,transform.rotation 實際上是一個 CALayer 用於處理動畫變換的虛擬屬性。
//
// ViewController.m
// CAKeyframeAnimation--+CAShapeLayer+UIBezierPath用法
//
// Created by 瞿傑 on 2017/3/24.
// Copyright © 2017年 yiniu. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic , strong) UIView * contentView ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width ;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height ;
self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,screenWidth , screenHeight)];
self.contentView.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.contentView];
// 新增一個滑動的圖層
CALayer * slipLayer = [CALayer layer];
slipLayer.frame = CGRectMake(0, 0, 50, 50);
slipLayer.position = CGPointMake(160, 150);
slipLayer.contents = (__bridge id)[UIImage imageNamed:@"icon_tmpImageName"].CGImage ;
slipLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_4);
[self.contentView.layer addSublayer:slipLayer];
// 做旋轉功能
CABasicAnimation * basicAnimation = [CABasicAnimation animation];
basicAnimation.keyPath = @"transform.rotation";
basicAnimation.repeatCount = 100 ;
basicAnimation.duration = 3.0;
basicAnimation.byValue = @(M_PI * 2);
[slipLayer addAnimation:basicAnimation forKey:nil];
}
2.4 、CAAnimationGroup 動畫組
CAAnimationGroup是另一個繼承於CAAnimation的子類,它添加了一個animations陣列的屬性,用來組合別的動畫。
//
// ViewController.m
// CAAnimationGroup-動畫組
//
// Created by 瞿傑 on 2017/3/26.
// Copyright © 2017年 yiniu. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,strong) UIView * contentView ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.contentView = [[UIView alloc] initWithFrame:CGRectMake(10, 50, 300, 300)];
self.contentView.backgroundColor = [UIColor brownColor];
[self.view addSubview:self.contentView];
// 1.設定曲線
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 150)];
[bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 300) controlPoint2:CGPointMake(225, 0)];
CAShapeLayer * shpaeLayer = [CAShapeLayer layer];
shpaeLayer.path = bezierPath.CGPath ;
shpaeLayer.fillColor = [UIColor blueColor].CGColor;
shpaeLayer.strokeColor = [UIColor redColor].CGColor ;
shpaeLayer.lineWidth = 3.0;
[self.contentView.layer addSublayer:shpaeLayer];
// 2.設定顏色漸變的Layer
CALayer * colorLayer = [CALayer layer];
colorLayer.frame = CGRectMake(0, 0, 50, 50);
colorLayer.position = CGPointMake(0, 150);
colorLayer.backgroundColor = [UIColor greenColor].CGColor ;
[self.contentView.layer addSublayer:colorLayer];
// 3.新增動畫
// 3.1幀動畫,沿bezierPath.CGPath路徑滑動
CAKeyframeAnimation * keyAnimation = [CAKeyframeAnimation animation];
keyAnimation.path= bezierPath.CGPath ;
keyAnimation.keyPath = @"position";
keyAnimation.rotationMode = kCAAnimationRotateAuto ;
// 3.2改變顏色
CABasicAnimation * colorAnimation = [CABasicAnimation animation];
colorAnimation.keyPath = @"backgroundColor";
colorAnimation.toValue = (__bridge id)[UIColor blueColor].CGColor ;
// 3.3建立一個動畫組容器
CAAnimationGroup * animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[keyAnimation,colorAnimation];
animationGroup.duration = 4.0 ;
animationGroup.repeatCount = 100 ;
// 3.4新增動畫
[colorLayer addAnimation:animationGroup forKey:nil];
}
2.5、CATransition過渡動畫
UIView 也可以做過渡動畫,具體請看UIView裡面的介紹筆記。
效果:當點選圖片後,當前的那個圖片漸變到沒有,同時另一張從沒有漸變到完成顯示。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic , strong) UIImageView * imageView ;
@property (nonatomic , strong) NSArray * images ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.imageView = [[UIImageView alloc] init];
self.imageView.frame = CGRectMake(100, 100, 100, 100);
self.imageView.userInteractionEnabled = YES ;
[self.imageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(switchImage)]];
[self.view addSubview:self.imageView];
self.images = @[[UIImage imageNamed:@"icon_xl"],
[UIImage imageNamed:@"nav_more"],
[UIImage imageNamed:@"slider_table"],
];
self.imageView.image = self.images[0];
}
-(void)switchImage
{
// 1.用CATransition做過渡動畫
CATransition * transition = [CATransition animation];
transition.duration = 1.0;
transition.type = kCATransitionFade ;
transition.subtype = kCATransitionFromLeft ;
[self.imageView.layer addAnimation:transition forKey:nil];
// cycle to next image
UIImage * currentImage = self.imageView.image ;
NSInteger index = [self.images indexOfObject:currentImage];
index = (index + 1)%[self.images count];
self.imageView.image = self.images[index];
}
3)移除layer圖層的動畫
3.1、在動畫進行中時移除動畫:
// 移除layer的某一屬性或虛擬屬性名 設定的動畫
- (void)removeAnimationForKey:(NSString *)key;
// 移除layer圖層所有的動畫
- (void)removeAllAnimations;
3.2、在動畫結束後移除動畫
當動畫結束後系統會自動移除動畫;除非設定 layer.removedOnCompletion = NO ,只有當需要移時手動刪除動畫,否則它會一直存在於記憶體中直到圖層被銷燬。
三、動畫 緩衝功能CAMediaTimingFunction
1)系統動畫緩衝
1.1 構造方法:+timingFunctionWithName:(NSString *)name;
name可以傳入的引數:
kCAMediaTimingFunctionLinear
kCAMediaTimingFunctionEaseIn
kCAMediaTimingFunctionEaseOut
kCAMediaTimingFunctionEaseInEaseOut
kCAMediaTimingFunctionDefault
kCAMediaTimingFunctionLinear 選項建立了一個線性的計時函式,同樣也是CAAnimation的timingFunction屬性為空時候的預設函式。線性步調使動畫立即加速到最大速度並且保持勻速達到終點。
kCAMediaTimingFunctionEaseIn 常量建立了一個開始慢慢加速然後達到最大速度後保持勻速達到終點的過程。
kCAMediaTimingFunctionEaseOut 與上面那個常量效果相反,一開始立即加速到最大速度,然後到終點之前開始減速的過程。
kCAMediaTimingFunctionEaseInEaseOut 建立一個慢慢加速到最大速度然後並持勻速,最後到終點之前開始慢慢減速的過程。是大多數預設的選擇。實際上當使用UIView的動畫方法時,它的確是預設的,但當建立 CAAnimation 的時候,就需要手動設定它了。
kCAMediaTimingFunctionDefault ,它和kCAMediaTimingFunctionEaseInEaseOut很類似,但是加速和減速的過程稍微有些慢。它和kCAMediaTimingFunctionEaseInEaseOut的區別很難察覺,可能蘋果覺得kCAMediaTimingFunctionEaseInEaseOut 對於隱式動畫來說更合適(然後對UIKit就改變想法,而是使用kCAMediaTimingFunctionEaseInEaseOut 做為預設效果),雖然它的名字說是預設的,但還是要記住當建立顯示的 CAAnimation 動畫預設是用 kCAMediaTimingFunctionDefault作為它們的計時方法。
1.2、例子
例子:效果是:三角形圖 先 慢慢的加速,然後達到最大速度後勻速,最後快點終點減速。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic , strong) UIView * contentView ;
@property (nonatomic , strong) CALayer * slipLayer ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width ;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height ;
self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,screenWidth , screenHeight)];
self.contentView.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.contentView];
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 150)];
[bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(300.0*1/4, 0) controlPoint2:CGPointMake(300.0*3/4, 300)];
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
shapeLayer.path = bezierPath.CGPath ;
shapeLayer.lineWidth = 3.0 ;
// shapeLayer.backgroundColor = [UIColor grayColor].CGColor ;
shapeLayer.fillColor = [UIColor blueColor].CGColor ;
shapeLayer.strokeColor = [UIColor redColor].CGColor ;
[self.contentView.layer addSublayer:shapeLayer];
// 新增一個滑動的圖層
CALayer * slipLayer = [CALayer layer];
slipLayer.frame = CGRectMake(0, 0, 50, 50);
slipLayer.position = CGPointMake(0, 150);
slipLayer.contents = (__bridge id)[UIImage imageNamed:@"icon_tmpImageName"].CGImage ;
slipLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_4);
[self.contentView.layer addSublayer:slipLayer];
self.slipLayer = slipLayer;
// 知識點1:
// 根據position建立一個(圖層的中心點)關建幀動畫,
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyframeAnimation.duration = 4.0 ;
keyframeAnimation.path = bezierPath.CGPath ;
keyframeAnimation.repeatCount = 100 ;
// 把rotationMode設定=kCAAnimationRotateAuto 根據曲線的切線自動旋轉
keyframeAnimation.rotationMode = kCAAnimationRotateAuto ;
// 手動建立一個緩衝方式
keyframeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// keyframeAnimation.speed = 2.0 ;
// slipLayer 新增一個動畫
[slipLayer addAnimation:keyframeAnimation forKey:nil];
}
2)自定義動畫緩衝(使用三次貝賽爾曲線 方式)
2.1、構造方法:
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
或 初始化方法 - (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
提示:這些引數範圍(0.0 ~ 1.0),點(c1x,c1y)代表第一個控制點,點(c2x,c2y)代表第二個控制點。cx 是動畫的時間比例,cy是動畫改變的量,斜率是動畫的速度。
2.2、三次貝塞爾曲線 動畫 緩衝函式
CAMediaTimingFunction函式的主要原則在於它把輸入的時間轉換成起點和終點之間成比例的改變(範圍 0.0 ~ 1.0)。我們可以用一個簡單的圖示來解釋,橫軸代表時間(範圍 0.0 ~ 1.0),縱軸代表改變的量(範圍 0.0 ~ 1.0),斜率表示動畫的速度,於是線性的緩衝kCAMediaTimingFunctionLinear就是一條從起點開始簡單的斜線。
如圖,point1點代表 ( c1x , c1y ) 控制點,point2點代表 ( c2x , c2y ) 控制點 。如果一個動畫的 timingFunction屬性賦值了同CAMediaTimingFunction 構造的三次貝賽爾曲線緩衝對像,那麼該動畫會: 一開始很快的加速,然後在中間的一段動畫會減速,最後快到終點的一段動畫再加速(即 快 --> 慢 --> 快);2.3、獲取緩衝方式的控制點 ,idx 範圍包含 0 ~ 3 ,ptr CGPoint 的結構體 - (void)getControlPointAtIndex:(size_t)idx values:(float[2])ptr;
//不知道為什麼pointControll1 和 pointCo