總的來講,從涉及類的形式來看,iOS動畫有:基於UIView的仿射形變更畫,基於CAAnimation及其子類的動畫,基於CG的動畫。這篇文章着重總結前兩種動畫。數組
設置UIView形變更畫有兩種常見用到的屬性,.frame
,.transform
,因此有的人也能夠分別稱之爲:bash
這兩種動畫只須要在動畫語法中適當的位置,基於UIView和CALayer的屬性設置變化值便可。這種動畫,不須要 調用核心動畫CAAnimation裏面的專用類和API。ide
其中,frame動畫設置方式有限,必須確切地制定形變先後的frame,平移還好,特別是 旋轉 的時候,只能經過數學知識計算出新的frame。這就得不償失了。因此,更多的時候,當涉及一些frame,bounds,center的改變或是形變的時候能夠用transform來取代frame。函數
//偏移動畫
[UIView beginAnimations:@"move" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
imageContainView.frame = CGRectMake(80, 80, 200, 200);
[label1 setBackgroundColor:[UIColor yellowColor]];
[label1 setTextColor:[UIColor redColor]];
[UIView commitAnimations];
複製代碼
//縮放動畫
view.transform = CGAffineTransformIdentity;
[UIView animateWithDuration:1.0f animations:^{
view.transform = CGAffineTransformMakeScale(2.0f, 2.0f);
}];
複製代碼
CGAffineTransform transform = CGAffineTransformScale(imageContainView.transform, 1.2, 1.2);
[UIView beginAnimations: @"scale"context: nil];
[UIView setAnimationDuration: 2];
[UIView setAnimationDelegate: self];
[imageView setTransform: transform];
[UIView commitAnimations];
複製代碼
.layer.transform
能夠在3D模式下面的變化,一般使用的都是前綴爲CATransform3D的類。imageView.layer.transform = CATransform3DIdentity;
[UIView animateWithDuration:1.0f animations:^{
imageView.layer.transform = CATransform3DMakeScale(2.0, 2.0, 1.0);
}];
複製代碼
下面是UIView的一些屬性介紹佈局
@property(nonatomic) CGRect frame;
@property(nonatomic) CGRect bounds; // default bounds is zero origin, frame size. animatable
@property(nonatomic) CGPoint center; // center is center of frame. animatable
@property(nonatomic) CGAffineTransform transform; // default is CGAffineTransformIdentity. animatable
@property(nonatomic) CGFloat contentScaleFactor NS_AVAILABLE_IOS(4_0);
@property(nonatomic,getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled __TVOS_PROHIBITED; // default is NO
@property(nonatomic,getter=isExclusiveTouch) BOOL exclusiveTouch __TVOS_PROHIBITED; // default is NO
複製代碼
在實際開發中,使用場景:動畫
(1) 當涉及一些frame, bounds, center的改變或是形變的時候能夠用 transform 來取代 frame。ui
(2) 通常在實際開發中都是平移,旋轉,縮放組合使用。this
下面是CALayer的一些屬性介紹atom
//寬度和高度
@property CGRect bounds;
//位置(默認指中點,具體由anchorPoint決定)
@property CGPoint position;
//錨點(x,y的範圍都是0-1),決定了position的含義
@property CGPoint anchorPoint;
//背景顏色(CGColorRef類型)
@property CGColorRef backgroundColor;
//形變屬性
@property CATransform3D transform;
//邊框顏色(CGColorRef類型)
@property CGColorRef borderColor;
//邊框寬度
@property CGFloat borderWidth;
//圓角半徑
@property CGFloat cornerRadius;
//內容(好比設置爲圖片CGImageRef)
@property(retain) id contents;
複製代碼
struct CGAffineTransform {
CGFloat a, b, c, d;
CGFloat tx, ty;
};
複製代碼
它其實表示的是一個矩陣:spa
由於最後一列老是是(0,0,1),因此有用的信息就是前面兩列。對一個view進行仿射變化就至關於對view上的每一個點作一個乘法,結果就是:
a表示x水平方向的縮放,tx表示x水平方向的偏移 d表示y垂直方向的縮放,ty表示y垂直方向的偏移 若是b和c不爲零的話,那麼視圖確定發生了旋轉,旋轉角度這樣計算:tan(angle) = b / a
若是這樣:
這個就是沒有變化的最初的樣子。
//還原
CGAffineTransformIdentity
//位移仿射 ---- 理解爲平移 (CGFloat tx,CGFloat ty)
CGAffineTransformMakeTranslation
CGAffineTransformTranslate
//旋轉仿射 ---- 理解爲旋轉 (CGFloat angle)
CGAffineTransformMakeRotation
CGAffineTransformRotate
//縮放仿射 --- 理解縮放大小 (CGFloat sx, CGFloat sy)
CGAffineTransformMakeScale
CGAffineTransformScale
複製代碼
CGAffineTransform操做的數學本質
//還原
CATransform3DIdentity
//位移3D仿射 ==> (CGFloat tx, CGFloat ty, CGFloat tz)
CATransform3DMakeTranslation
CATransform3DTranslation
//旋轉3D仿射 ==> (CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeRotation
CATransform3DRotation
//縮放3D仿射 ==> (CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale
CATransform3DScale
//疊加3D仿射效果
CATransform3DConcat
//仿射基礎3D方法,能夠直接作效果疊加
CGAffineTransformMake (sx,shx,shy,sy,tx,ty)
//檢查是否有作過仿射3D效果 == ((CATransform3D t))
CATransform3DIsIdentity(transform)
//檢查2個3D仿射效果是否相同
CATransform3DEqualToTransform(transform1,transform2)
//3D仿射效果反轉(反效果,好比原來擴大,就變成縮小)
CATransform3DInvert(transform)
複製代碼
//將一個CGAffinrTransform轉化爲CATransform3D
CATransform3D CATransform3DMakeAffineTransform (CGAffineTransform m);
//判斷一個CATransform3D是否能夠轉換爲CAAffineTransformbool
CATransform3DIsAffine (CATransform3D t);
//將CATransform3D轉換爲CGAffineTransform
CGAffineTransform CATransform3DGetAffineTransform (CATransform3D t);
複製代碼
鏈接設置兩個以上屬性的動畫,能夠先調用含有 formMake
的API,而後再調用只含 form
的API。例如,這樣:
alertView.transform = CGAffineTransformMakeScale(.25, .25);
alertView.transform = CGAffineTransformTranslate(alertView.transform, 0, 600);
複製代碼
另外,能夠直接利用 CGAffineTransformConcat 來組合多種含有 formMake
的形變API。
CGAffineTransform viewTransform = CGAffineTransformConcat(CGAffineTransformMakeScale(.25, .25), CGAffineTransformMakeTranslation(0, 600));
alertView.transform = viewTransform;
複製代碼
關於組合3D形變也有相應的API --- CATransform3DConcat,關於3D形變下一篇會專門介紹。
- (void)viewDidAppear: (BOOL)animated {
/// 初始化動畫開始前label的位置
CGFloat offset = label1.frame.size.height * 0.5;
label1.transform = CGAffineTransformConcat(
CGAffineTransformMakeScale(0, 0),
CGAffineTransformTranslate(0, -offset)
);
label1.alpha = 0;
[UIView animateWithDuration: 3. animations: ^ {
/// 還原label1的變換狀態並形變和偏移label2
label1.transform = CGAffineTransformIdentifier;
label1.transform = CGAffineTransformConcat(
CGAffineTransformMakeScale(0, 0),
CGAffineTransformTranslate(0, offset)
);
label1.alpha = 1;
label2.alpha = 0;
}];
}
複製代碼
組合變換的本質 CGAffineTransformConcat的數學本質是將括號內表明的若干變換的係數矩陣進行相乘。
另外一種組合變換 基於已有的CGAffineTransform連續追加新的CGAffineTransform:
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, 0.5f, 0.5f);
transform = CGAffineTransformRotate(transform, 30.0f/180.0f*M_PI);
transform = CGAffineTransformTranslate(transform, 200.0f, 0.0f);
layerView.layer.affineTransform = transform;
複製代碼
當咱們改變過一個view.transform屬性或者view.layer.transform的時候須要恢復默認狀態的話,記得先把他 們重置爲:
view.transform = CGAffineTransformIdentity;
view.layer.transform = CATransform3DIdentity;
複製代碼
官方文檔上關於transform屬性的說明:
Changes to this property can be animated. However, if the transform property contains a non-identity transform, the value of the frame property is undefined and should not be modified. In that case, you can reposition the view using the center property and adjust the size using the bounds property instead.
若是在程序中改變了某個控件的transform,那麼請不要使用這個控件的frame計算 子控件 的佈局,應該使用bounds+center代替。
CAAnimation——全部動畫對象的父類
/**
* 抖動效果
*/
-(void)shakeAnimation{
CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];//在這裏@"transform.rotation"==@"transform.rotation.z"
NSValue *value1 = [NSNumber numberWithFloat:-M_PI/180*4];
NSValue *value2 = [NSNumber numberWithFloat:M_PI/180*4];
NSValue *value3 = [NSNumber numberWithFloat:-M_PI/180*4];
anima.values = @[value1,value2,value3];
anima.repeatCount = MAXFLOAT;
[_demoView.layer addAnimation:anima forKey:@"shakeAnimation"];
}
複製代碼
CAAnimation{
CAPropertyAnimation{
CABasicAnimation{
CASpringAnimation
}
CAKeyframeAnimation
}
CATransition
CAAnimationGroup
}
複製代碼
是全部動畫對象的父類,負責控制動畫的持續時間和速度,是個抽象類,不能直接使用,應該使用它具體的子類
帶*號表明來自CAMediaTiming協議的屬性)
CAAnimation——動畫填充模式
默認爲YES,表明動畫執行完畢後就從圖層上移除,圖形會恢復到動畫執行前的狀態。若是想讓圖層保持顯示動畫執行後的狀態,那就設置爲NO,不過還要設置fillMode爲kCAFillModeForwards
CAAnimation——控制恢復到動畫執行前
要想fillMode有效,最好設置removedOnCompletion = NO
若是 fillMode = kCAFillModeForwards
同時 removedOnComletion = NO
,那麼在動畫執行完畢後,圖層會保持顯示動畫執行後的狀態。但在實質上,圖層的屬性值仍是動畫執行前的初始值,並無真正被改變。
CAAnimation——動畫速度控制函數
基本動畫,是CAPropertyAnimation的子類
CATransform3D{
//rotation旋轉
transform.rotation.x
transform.rotation.y
transform.rotation.z
//scale縮放
transform.scale.x
transform.scale.y
transform.scale.z
//translation平移
transform.translation.x
transform.translation.y
transform.translation.z
}
CGPoint{
position
position.x
position.y
}
CGRect{
bounds
bounds.size
bounds.size.width
bounds.size.height
bounds.origin
bounds.origin.x
bounds.origin.y
}
property{
opacity
backgroundColor
cornerRadius
borderWidth
contents
Shadow{
shadowColor
shadowOffset
shadowOpacity
shadowRadius
}
}
複製代碼
//心臟縮放動畫
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; //選中的這個keyPath就是縮放
scaleAnimation.fromValue = [NSNumber numberWithDouble:0.5]; //一開始時是0.5的大小
scaleAnimation.toValue = [NSNumber numberWithDouble:1.5]; //結束時是1.5的大小
scaleAnimation.duration = 1; //設置時間
scaleAnimation.repeatCount = MAXFLOAT; //重複次數
[_heartImageView.layer addAnimation:scaleAnimation forKey:@"CQScale"]; //添加動畫
複製代碼
//風車旋轉動畫
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.fromValue = [NSNumber numberWithDouble:0.f];
rotationAnimation.toValue = [NSNumber numberWithDouble:2 * M_PI];
rotationAnimation.duration = 2.f;
rotationAnimation.repeatCount = MAXFLOAT;
[_fengcheImageView.layer addAnimation:rotationAnimation forKey:@"CQRotation"];
複製代碼
//平移動畫
CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
positionAnimation.fromValue = [NSNumber numberWithDouble:0.f];
positionAnimation.toValue = [NSNumber numberWithDouble:SCREEN_WIDTH];
positionAnimation.duration = 2;
positionAnimation.repeatCount = MAXFLOAT;
[_arrowImageView.layer addAnimation:positionAnimation forKey:@"CQPosition"];
複製代碼
//根據values移動的動畫
CAKeyframeAnimation *catKeyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
CGPoint originalPoint = self.catImageView.layer.frame.origin;
CGFloat distance = 50;
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(originalPoint.x + distance, originalPoint.y + distance)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(originalPoint.x + 2 * distance, originalPoint.y + distance)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(originalPoint.x + 2 * distance, originalPoint.y + 2 * distance)];
NSValue *value4 = [NSValue valueWithCGPoint:originalPoint];
catKeyAnimation.values = @[value4, value1, value2, value3, value4];
catKeyAnimation.duration = 2;
catKeyAnimation.repeatCount = MAXFLOAT;
catKeyAnimation.removedOnCompletion = NO;
[self.catImageView.layer addAnimation:catKeyAnimation forKey:nil];
複製代碼
//指定path
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 200, 200, 200)];
//指定path的動畫
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(100, 100)];
[path2 addLineToPoint:CGPointMake(100, 200)];
[path2 addLineToPoint:CGPointMake(200, 200)];
[path2 addLineToPoint:CGPointMake(200, 100)];
[path2 addLineToPoint:CGPointMake(100, 100)];
CAKeyframeAnimation *penguinAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
penguinAnimation.path = path2.CGPath;
penguinAnimation.duration = 2;
penguinAnimation.repeatCount = MAXFLOAT;
penguinAnimation.removedOnCompletion = NO;
[self.penguinImageView.layer addAnimation:penguinAnimation forKey:nil];
複製代碼
上面單一動畫的狀況在實際開發中實際比較少,更多的時候是組合這些動畫:建立不一樣類型的動畫對象,設置好它們的參數,而後把這些動畫對象存進數組,傳進組動畫對象的animations屬性中去。
//建立組動畫
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.duration = 3;
animationGroup.repeatCount = MAXFLOAT;
animationGroup.removedOnCompletion = NO;
/* beginTime 能夠分別設置每一個動畫的beginTime來控制組動畫中每一個動畫的觸發時間,時間不可以超過動畫的時間,默認都爲0.f */
//縮放動畫
CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
animation1.values = @[[NSNumber numberWithFloat:1.0],[NSNumber numberWithFloat:0.5],[NSNumber numberWithFloat:1.5],[NSNumber numberWithFloat:1.0]];
animation1.beginTime = 0.f;
//按照圓弧移動動畫
CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(300, 200)];
[bezierPath addQuadCurveToPoint:CGPointMake(200, 300) controlPoint:CGPointMake(300, 300)];
[bezierPath addQuadCurveToPoint:CGPointMake(100, 200) controlPoint:CGPointMake(100, 300)];
[bezierPath addQuadCurveToPoint:CGPointMake(200, 100) controlPoint:CGPointMake(100, 100)];
[bezierPath addQuadCurveToPoint:CGPointMake(300, 200) controlPoint:CGPointMake(300, 100)];
animation2.path = bezierPath.CGPath;
animation2.beginTime = 0.f;
//透明度動畫
CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation3.fromValue = [NSNumber numberWithDouble:0.0];
animation3.toValue = [NSNumber numberWithDouble:1.0];
animation3.beginTime = 0.f;
//添加組動畫
animationGroup.animations = @[animation1, animation2,animation3];
[_penguinImageView.layer addAnimation:animationGroup forKey:nil];
複製代碼
前面關鍵幀動畫章節提到了貝塞爾曲線,這個曲線頗有用,在iOS開發中有兩種形式可用:CGMutablePathRef和UIBezierPath,都可以經過制定控制點數組的形式惟一肯定曲線,也能夠經過矩形內切橢圓惟一肯定曲線。下面是二者的例子:
// 貝塞爾曲線關鍵幀
// 設置路徑, 繪製貝塞爾曲線
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 200, 200); // 起始點
CGPathAddCurveToPoint(path, NULL, 100, 300, 300, 500, 200, 600);
// CGPathAddCurveToPoint(path, NULL, 控制點1.x, 控制點1.y, 控制點2.x, 控制點2.y, 終點.x, 終點.y);
// 設置path屬性
keyframeAnimation.path = path;
CGPathRelease(path);
// 設置其餘屬性
keyframeAnimation.duration = 4;
keyframeAnimation.beginTime = CACurrentMediaTime() + 1; // 設置延遲2秒執行, 不設置這個屬性, 默認直接執行
// 3. 添加動畫到圖層, 會自動執行
[_layer addAnimation:keyframeAnimation forKey:@"GGKeyframeAnimation"];
複製代碼
CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(SCREEN_WIDTH/2-100, SCREEN_HEIGHT/2-100, 200, 200)];
anima.path = path.CGPath;
anima.duration = 2.0f;
[_demoView.layer addAnimation:anima forKey:@"pathAnimation"];
複製代碼
-(void)pathAnimation2{
//建立path
UIBezierPath *path = [UIBezierPath bezierPath];
//設置線寬
path.lineWidth = 3;
//線條拐角
path.lineCapStyle = kCGLineCapRound;
//終點處理
path.lineJoinStyle = kCGLineJoinRound;
//多條直線
[path moveToPoint:(CGPoint){0, SCREEN_HEIGHT/2-50}];
[path addLineToPoint:(CGPoint){SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50}];
[path addLineToPoint:(CGPoint){SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50}];
[path addLineToPoint:(CGPoint){SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50}];
[path addLineToPoint:(CGPoint){SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50}];
[path addLineToPoint:(CGPoint){SCREEN_WIDTH, SCREEN_HEIGHT/2-50}];
// [path closePath];
CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
anima.path = path.CGPath;
anima.duration = 2.0f;
anima.fillMode = kCAFillModeForwards;
anima.removedOnCompletion = NO;
anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[_demoView.layer addAnimation:anima forKey:@"pathAnimation"];
}
複製代碼