看著我這標(biāo)題就覺得有點狂啊,其實是本人最近工作有點閑,看了很多人寫的sample,從中萃取精華,總結(jié)下,以防止自己忘記了,豈不白白浪費了這幾天的努力。廢話就這么多,現(xiàn)在開始正題:
先說說咱這篇文章會講到什么吧,首先我會講講繪圖,然后講講動畫
咱們首先從繪圖說起,
- 使用CoreGraphics進(jìn)行繪圖
說到繪圖,那繪圖是在哪里繪呢,當(dāng)然是在View里面了,我們可以重寫UIView的- (void)drawRect:(CGRect)rect方法,然后在里面進(jìn)行繪圖,繪圖的話就離不來CoreGraphics了,然而CoreGraphics的核心就是QuzCore里面的幾個函數(shù)了。廢話少說,直接上代碼
- (void)drawRect:(CGRect)rect {
// 獲取繪圖上下文環(huán)境
CGContextRef context = UIGraphicsGetCurrentContext();
// 設(shè)置線條的寬度
CGContextSetLineWidth(context, 4.0);
// 設(shè)置線條的顏色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
// 將繪畫筆移到一個點作為繪圖的起點
CGContextMoveToPoint(context, 20.0, 20.0);
// 由起點畫一條線到終點
CGContextAddLineToPoint(context, 300, 400);
// 畫出來
CGContextStrokePath(context);
}
這段代碼之后呈現(xiàn)出的效果如下:

如你所見,這段代碼的功能是畫了一條紅色的斜線。當(dāng)然如果你想畫一個矩形的話也是同樣的一個道理,在上面的代碼后面加上如下代碼就好:
// 畫一個矩形
CGRect arect = CGRectMake(200, 30, 49, 59);
// 設(shè)置填充色
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
// 填充矩形
CGContextFillRect(context, arect);
// 將舉行顯示出來
CGContextAddRect(context, arect);
效果的話自己去試吧,我就不上圖了,另外誰能告訴我怎么把圖片縮小,這太占地了。繪圖就說這么多了,感覺用處不大啊,以后感覺用處大再補充吧。
-
使用CAShapeLayer 、UIBezierPath、CABasicAnimation畫
經(jīng)??吹絼e人發(fā)出一下比較屌的動畫例子,其中用到的兩個必不可少的技術(shù)點有三個,分別是:
CAShapeLayer:這是動畫的主要載體,動畫都是由他執(zhí)行
UIBezierPath:用來描繪CAShapeLayer的邊界
CABasicAnimation:動畫對象,由他對動畫的執(zhí)行過程進(jìn)行描述
使用實例如下:
#import "NibView.h"
@interface NibView()// 執(zhí)行動畫的載體 @property (nonatomic, strong) CAShapeLayer *shapLayer; @end @implementation NibView /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ // 畫面的初始化在此方法內(nèi)進(jìn)行 - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; self.frame = [UIApplication sharedApplication].keyWindow.bounds; self.backgroundColor = [UIColor redColor]; UIBezierPath *bpath = [UIBezierPath bezierPath]; [bpath moveToPoint:CGPointMake(100, 150)]; [bpath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(200, 80) controlPoint2:CGPointMake(200, 200)]; [bpath addCurveToPoint:CGPointMake(300, 400) controlPoint1:CGPointMake(400, 230) controlPoint2:CGPointMake(250, 350)]; [bpath addLineToPoint:CGPointMake(100, 400)]; [bpath closePath]; self.shapLayer.path = bpath.CGPath; [self.layer addSublayer:_shapLayer]; return self; } // 獲得一個path - (UIBezierPath *)rectPath { UIBezierPath *rectpath = [UIBezierPath bezierPath]; [rectpath moveToPoint:CGPointMake(100.0, 150.0)]; [rectpath addLineToPoint:CGPointMake(300, 150)]; [rectpath addLineToPoint:CGPointMake(300, 400)]; [rectpath addLineToPoint:CGPointMake(100, 400)]; [rectpath closePath]; return rectpath; } // 執(zhí)行動畫 - (void)animate { CABasicAnimation *expandAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; expandAnimation.fromValue = (__bridge id)(_shapLayer.path); expandAnimation.toValue = (__bridge id)[self rectPath].CGPath; expandAnimation.beginTime = 0; expandAnimation.duration = 0.5; expandAnimation.fillMode = kCAFillModeForwards; expandAnimation.removedOnCompletion = NO; [self.shapLayer addAnimation:expandAnimation forKey:nil]; } // 加個按鈕讓動畫可以重復(fù)進(jìn)行 - (IBAction)goAnimate:(UIButton *)sender { [self animate]; } #pragma mark - initViews - (CAShapeLayer *)shapLayer { if (!_shapLayer) { _shapLayer = [[CAShapeLayer alloc] init]; _shapLayer.frame = self.bounds; _shapLayer.fillColor = [UIColor greenColor].CGColor; } return _shapLayer; } @end
下面對上面的代碼進(jìn)行說明:
- 我這里是將代碼動畫放在一個xib文件里面的,需要注意的是當(dāng)xib文件被加載時它調(diào)用的是- (id)initWithCoder:(NSCoder *)aDecoder這個方法,而不是- (id)initWithFrame:(CGRect)frame這個方法,使用初始化的事情都應(yīng)該放在這里面來進(jìn)行。
- 在animate這個方法里面有這么兩行
expandAnimation.fillMode = kCAFillModeForwards;
expandAnimation.removedOnCompletion = NO;
需要注意的是這兩行要同時寫上動畫才不會回去。
動畫執(zhí)行的效果如下:

另外你可以利用CAAnimationGroup來組織來管理動畫,使得幾個動畫連續(xù)執(zhí)行,這樣可以形成一系列的連貫動畫效果,例如:
#import "NibView.h"
@interface NibView()
// 執(zhí)行動畫的對戲那個
@property (nonatomic, strong) CAShapeLayer *shapLayer;
@end
@implementation NibView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
// 畫面的初始化在此方法內(nèi)進(jìn)行
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
self.frame = [UIApplication sharedApplication].keyWindow.bounds;
self.backgroundColor = [UIColor redColor];
self.shapLayer.path = [self curvePath].CGPath;
[self.layer addSublayer:_shapLayer];
return self;
}
// 獲得一個飽含曲線的path
- (UIBezierPath *)curvePath {
UIBezierPath *bpath = [UIBezierPath bezierPath];
[bpath moveToPoint:CGPointMake(70, 150)];
[bpath addCurveToPoint:CGPointMake(270, 150)
controlPoint1:CGPointMake(170, 80)
controlPoint2:CGPointMake(170, 200)];
[bpath addCurveToPoint:CGPointMake(270, 400)
controlPoint1:CGPointMake(370, 230)
controlPoint2:CGPointMake(220, 350)];
[bpath addLineToPoint:CGPointMake(70, 400)];
[bpath closePath];
return bpath;
}
// 獲得一個path
- (UIBezierPath *)rectPath {
UIBezierPath *rectpath = [UIBezierPath bezierPath];
[rectpath moveToPoint:CGPointMake(100.0, 150.0)];
[rectpath addLineToPoint:CGPointMake(300, 150)];
[rectpath addLineToPoint:CGPointMake(300, 400)];
[rectpath addLineToPoint:CGPointMake(100, 400)];
[rectpath closePath];
return rectpath;
}
// 獲得一個正方形邊跡
- (UIBezierPath *)suqarePath {
UIBezierPath *squarPath = [UIBezierPath bezierPath];
[squarPath moveToPoint:CGPointMake(10, 150)];
[squarPath addLineToPoint:CGPointMake(310, 150)];
[squarPath addLineToPoint:CGPointMake(310, 450)];
[squarPath addLineToPoint:CGPointMake(10, 450)];
[squarPath closePath];
return squarPath;
}
// 執(zhí)行動畫
- (void)animate {
// 原始形態(tài)變成長方形
CABasicAnimation *expandAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
expandAnimation.fromValue = (__bridge id)(_shapLayer.path);
expandAnimation.toValue = (__bridge id)[self rectPath].CGPath;
expandAnimation.beginTime = 0;
expandAnimation.duration = 0.5;
[self.shapLayer addAnimation:expandAnimation forKey:nil];
// 長方形變成正方形
CABasicAnimation *expandAnimation2 = [CABasicAnimation animationWithKeyPath:@"path"];
expandAnimation2.fromValue = (__bridge id)[self rectPath].CGPath;
expandAnimation2.toValue = (__bridge id)[self suqarePath].CGPath;
expandAnimation2.beginTime = expandAnimation.beginTime + expandAnimation.duration;
expandAnimation2.duration = 0.5;
// 正方形又回到原始形態(tài)
CABasicAnimation *expandAnimation3 = [CABasicAnimation animationWithKeyPath:@"path"];
expandAnimation3.fromValue = (__bridge id)[self suqarePath].CGPath;
expandAnimation3.toValue = (__bridge id)(_shapLayer.path);
expandAnimation3.beginTime = expandAnimation2.beginTime + expandAnimation2.duration;
expandAnimation3.duration = 0.5;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = @[expandAnimation, expandAnimation2, expandAnimation3];
group.beginTime = expandAnimation.beginTime;
group.duration = expandAnimation3.beginTime + expandAnimation3.duration;
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.repeatCount = 2;
[self.shapLayer addAnimation:group forKey:nil];
}
// 加個按鈕讓動畫可以重復(fù)進(jìn)行
- (IBAction)goAnimate:(UIButton *)sender {
[self animate];
}
#pragma mark - initViews
- (CAShapeLayer *)shapLayer {
if (!_shapLayer) {
_shapLayer = [[CAShapeLayer alloc] init];
_shapLayer.frame = self.bounds;
_shapLayer.fillColor = [UIColor greenColor].CGColor;
}
return _shapLayer;
}
@end
由于修改較大,使用干脆又重現(xiàn)全部貼了出來了,下面做以下說明:
- 首先為了代碼的整齊我把initWithCoder里面的畫邊界部分的代碼抽了出來,單獨形成了一個方法
- 我增加了一個返回正方形的軌跡方法suqarePath
- 我對animate方法進(jìn)行了修改,引入了CAAnimationGroup,這里仍需要注意的是fillMode和removedOnCompletion這兩個熟悉,現(xiàn)在把他應(yīng)用到動畫組上。
通過動畫組,我們可以形成各種細(xì)膩的動畫效果,一切貌似變的明朗起來.
-
繞著Z軸旋轉(zhuǎn)的方法
有一種動畫叫做旋轉(zhuǎn),對于旋轉(zhuǎn)來說如果像上面一樣一個個慢慢組的話會寫死你有木有。那旋轉(zhuǎn)動畫怎么玩呢,很簡單,看到我們上面聲明動畫是這么玩的:
[CABasicAnimation animationWithKeyPath:@"path"],
然而,現(xiàn)在我們將這么玩:
[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]
他們兩的區(qū)別就是KeyPath變了,看到這里,我們明白了,其實動畫的種類就是由這個東西決定的。是@"path"說明這個動畫是通過改變邊界來形成動畫,那很自然@"transform.rotation.z"就是繞著Z軸旋轉(zhuǎn)形成的動畫了。廢話不多說,再次上代碼:
首先在類中加入如下方法
// 旋轉(zhuǎn)動畫
- (void)rotation {
CABasicAnimation *rotaionAnimate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotaionAnimate.toValue = @(M_PI * 2.0);
rotaionAnimate.duration = 0.5;
rotaionAnimate.removedOnCompletion = YES;
[self.shapLayer addAnimation:rotaionAnimate forKey:nil];}
然后在修改goAnimate方法
- (CAShapeLayer *)shapLayer {
if (!_shapLayer) {
_shapLayer = [[CAShapeLayer alloc] init];
_shapLayer.frame = self.bounds;
_shapLayer.fillColor = [UIColor greenColor].CGColor;
}
return _shapLayer;
}
這里我們讓圖層旋轉(zhuǎn)360度,運行效果如下:

另外需要說明的是可以制定圖像繞著那一點旋轉(zhuǎn),我們只要指定它的描點,在rotation方法里面加入下面這句代碼
self.shapLayer.anchorPoint = CGPointMake(0.1, 0.1);
效果如下:

由于我們改變了描點,所以圖像的fram變了。
- 描邊動畫
有關(guān)描邊動畫也是改變一下KeyPath,它的KeyPath是:@"strokeEnd"
在代碼中加入如下方法
- (void)strokeBoard {
self.shapLayer.strokeColor = [UIColor whiteColor].CGColor;
self.shapLayer.lineWidth = 20.0;
CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
strokeAnimation.fromValue = @0.0;
strokeAnimation.toValue = @1.0;
strokeAnimation.duration = 1.0;
[self.shapLayer addAnimation:strokeAnimation forKey:nil];
}
然后在goAnimate方法里面加入如下代碼
[NSTimer scheduledTimerWithTimeInterval:4.5
target:self
selector:@selector(strokeBoard)
userInfo:nil
repeats:NO];
這里strokeBoard方法里面我們設(shè)置了shapLayer的lineWidth和strokeColor,lineWidth默認(rèn)是0,strokeColor默認(rèn)是透明的。
當(dāng)lineWidth非0時,顯示方法是內(nèi)外各一半。為了便于觀察,這里運行前把上面的描點的那句代碼注釋掉,運行效果如下:

- CAKeyframeAnimation:關(guān)鍵幀動畫。(和CABasicAnimation平行)就是一幀一幀的動畫了,一般用來處理GIF圖片的
首先可以在View里面加入一張圖片,然后讓圖片抖動起來,抖動但動畫代碼如下:
// 抖動動畫
- (void)animate {
// 定義幀動畫
CAKeyframeAnimation animate = [CAKeyframeAnimation animation];
// 改變弧度
animate.values = @[@(M_PI/1805),@(-M_PI/1805),@(M_PI/1805)];
// 關(guān)鍵幀是什么必須有,其實就是和什么有關(guān)的動畫了
animate.keyPath = @"transform.rotation";
// 重復(fù)次數(shù)
animate.repeatCount = MAXFLOAT;
animate.duration = 0.2;
[_imageView.layer addAnimation:animate forKey:nil];
}
最后實現(xiàn)的效果如下:

-
動畫但暫停
如果動畫時加在layer上的話,動畫是可以被暫停的,只需要將執(zhí)行動畫的那個layer的speed屬性設(shè)置為0就好,要回復(fù)的話設(shè)置成1.0就好
如暫停和恢復(fù)上面的動畫:
- (void)stopAnimate {// 獲取暫停時間 // CFTimeInterval pausedTime = [self.imageView.layer convertTime:CACurrentMediaTime() fromLayer:nil]; // // self.imageView.layer.timeOffset = pausedTime; if (self.imageView.layer.speed == 0) { self.imageView.layer.speed = 1.0; } else { self.imageView.layer.speed = 0; } }