自己學習用的,做一個簡單的匯總。
1.Quartz2D
提起iOS中的繪圖控件,必然會想到Quartz2D。Quartz 2D是?個二維繪圖引擎,同時支持iOS和Mac系統(tǒng)。Quartz2D的API來自于CoreGraphics框架,數(shù)據(jù)類型和函數(shù)基本都以CG作為前綴,如CGContextRef、CGPathRef。
Quartz 2D能完成的工作:
- 繪制圖形 : 線條\三角形\矩形\圓\弧等
- 繪制文字
- 繪制\生成圖片(圖像)
- 讀取\生成PDF
- 截圖\裁剪圖片
- 自定義UI控件
一般在開發(fā)中,給圖片添加水印,合并圖片,自定義UI控件(UIKit框架中沒有的)等。
一般情況下,通過蘋果官方的UIKit庫就可以搭建常見的UI界面了,但是如果通過UIKit框架不能實現(xiàn)的一些UI界面,就需要我們自己來通過Quartz2D進行繪制了。
自定義UI控件是Quartz2D中非常重要的一個功能
使用Quartz2D 繪圖有2種方式:
方式一:直接調(diào)用Quartz2D的API 進行繪圖
方式二:調(diào)用 UIKit 框架封裝好的 API 進行繪圖(代碼簡單,但是只能是文字和圖片)
繪圖的基本步驟:
1.開啟圖形上下文(如果是自定義view,不需要這一步,但是必須在drawRect方法中獲取context,在這里才能獲取和view相關(guān)聯(lián)的context)
UIGraphicsBeginImageContext(size)
2.獲取圖形上下文
CGContextRef context = UIGraphicsGetCurrentContext();
3.設(shè)置上下文的一些屬性(顏色,線寬)
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGContextSetLineJoin(context, kCGLineJoinRound);// 設(shè)置連接處的樣式
CGContextSetLineCap(context, kCGLineCapRound);// 設(shè)置頭尾的樣式
//UIColor本身提供了setStroke,setFill的方法
[[UIColor purpleColor] setStroke];
4.拼接路徑(繪制圖形)
1.
CGContextMoveToPoint(context, 50, 50);
CGContextAddLineToPoint(context, 100, 100);
2.
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 100, 100);
CGPathAddLineToPoint(path, NULL, 200, 200);
CGContextAddPath(context, path);
3.
UIBezierPath* path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(200, 200)];
CGContextAddPath(context, path.CGPath);
4.純OC方法
// 創(chuàng)建路徑對象
UIBezierPath* path = [UIBezierPath bezierPath];
// 拼接
[path moveToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(200, 200)];
[path setLineWidth:30];
[path setLineJoinStyle:kCGLineJoinBevel];
// 通過路徑對象 渲染
[path stroke];
5.渲染
CGContextStrokePath(context);
CGContextFillPath(context);
CGContextEOFillPath(context);
CGContextDrawPath(context,drawModel); //很多的模式,stroke、fill....
CGContextStrokeRectWithWidth(context.lineWidth);//stroke的同時,設(shè)置了線寬
CGContextStrokeLineSegments //畫線段
[UIBezierPath stroke];
6.關(guān)閉上下文(關(guān)閉路徑)
CGContextClosePath(context)
- (void)closePath; //oc
UIGraphicsEndImageContext()
iOS中提供了繪制各種圖形的方法
1.C代碼
CGContextAddLineToPoint //直線
CGContextAddCurveToPoint //曲線
CGContextAddQuadCurveToPoint //四方曲線(暫時先這樣翻譯吧)
CGContextAddRect //矩形
CGContextAddEllipseInRect //橢圓
CGContextAddArc //圓弧
2.OC代碼(UIBezierPath)
- (void)moveToPoint:(CGPoint)point;
- (void)addLineToPoint:(CGPoint)point;
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
示例代碼:
- 創(chuàng)建圓形圖片的方法:關(guān)于這塊有挺多東西的,需要的話可以參考iOS 高效添加圓角效果實戰(zhàn)講解,這篇帖子的評論部分也提供了好多別人的框架
- (instancetype)circleImage {
// 開啟圖形上下文
UIGraphicsBeginImageContext(self.size);
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 添加一個圓
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextAddEllipseInRect(ctx, rect);
// 裁剪
CGContextClip(ctx);
// 繪制圖片
[self drawInRect:rect];
// 獲得圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉圖形上下文
UIGraphicsEndImageContext();
return image;
}
自定義view(自定義UI控件)
?定義view的步驟:
(1)新建?個類,繼承自UIView
(2)實現(xiàn)-(void)drawRect:(CGRect)rect?法
注:1.繪制的方法必須寫在drawRect:方法中,只有在這里才能獲取和view相關(guān)聯(lián)的上下文
2.drawRect:什么時候調(diào)用(不能自己調(diào)用,只能通過系統(tǒng)調(diào)用這個方法)。
當view第一次顯示到屏幕上時(被加到UIWindow上顯示出來)
調(diào)用view的setNeedsDisplay或者setNeedsDisplayInRect:時
自定義圓形進度條(drawRect實現(xiàn)):
1.進度值 -- 開放一個progressValue屬性
2.根據(jù)進度值,重繪UI界面 --[self setNeedsDisplay],然后系統(tǒng)會自動調(diào)用drawRect方法
- (void)setProgressValue:(CGFloat)progressValue {
_progressValue = progressValue;
[self setNeedsDisplay];// 重繪
}
- (void)drawRect:(CGRect)rect {
UIBezierPath* path = [UIBezierPath bezierPathWithArcCenter:self.center radius:self.frame.size.width < self.frame.size.height ? self.frame.size.width * 0.3 : self.frame.size.height * 0.3 startAngle:-M_PI_2 endAngle:2 * M_PI * self.progressValue - M_PI_2 clockwise:1];
[path setLineWidth:3];
[[UIColor blueColor] setStroke];
[path stroke];// 渲染
}
注:在drawRect方法中繪圖,
1.不需要創(chuàng)建圖形上下文。 -- 這里可以直接獲取到和view相關(guān)聯(lián)的上下文
2.使用UIBezierPath(OC方法),不需要獲取圖形上下文,內(nèi)部蘋果已經(jīng)封裝了
3.不需要關(guān)閉上下文
圓形進度條,在這里其實就可以和SDWebImage結(jié)合起來,實現(xiàn)一個圖片加載的進度顯示了。這里貼一個SDWebImage進度顯示簡單Demo
2. CALayer
在iOS中CALayer的設(shè)計主要是了為了內(nèi)容展示和動畫操作,CALayer本身并不包含在UIKit中,它不能響應事件。由于CALayer在設(shè)計之初就考慮它的動畫操作功能,CALayer很多屬性在修改時都能形成動畫效果,這種屬性稱為“隱式動畫屬性”。但是對于UIView的根圖層而言屬性的修改并不形成動畫效果,因為很多情況下根圖層更多的充當容器的做用,如果它的屬性變動形成動畫效果會直接影響子圖層。另外,UIView的根圖層創(chuàng)建工作完全由iOS負責完成,無法重新創(chuàng)建,但是可以往根圖層中添加子圖層或移除子圖層。
CALayer的基本使用
- 設(shè)置圓角
view.layer.cornerRadius = 5;
view.layer.maskToBound = YES;
注意,如果設(shè)置了maskToBounds=YES,那將不會有陰影效果
- 設(shè)置邊框
view.layer.boarderWidth = 5;
view.layer.boarderColor = [UIColor redColor].CGColor;
- 設(shè)置旋轉(zhuǎn)
view.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
- 設(shè)置陰影
view.layer.shadowColor = [UIColor grayColor].CGColor;
view.layer.shadowOffset = CGSizeMake(10, 10);
view.layer.shadowOpacity = 0.5;
注意,如果設(shè)置了maskToBounds=YES,那將不會有陰影效果
- 設(shè)置背景圖片
view.layer.content = [UIImage iamgeNamed:@"xxx"].CGImage;
自定義layer
layer和view是緊密相連的,所以他們的操作很相似。自定義layer需要重寫drawInContext方法,自定義view需要重寫drawRect方法。針對view的操作最終都會作用到layer上
- (void)drawInContext:(CGContextRef)ctx {
// 設(shè)置為藍色
CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);
//繪制圖形的方法和view是一樣的都是使用Quartz2D,參考Quartz2D部分
// 渲染
CGContextFillPath(ctx);
}
注意:需要刷新控件時,不能自己手動調(diào)用這個方法,而是使用[self setNeedsDisplay]
CALayer的mask屬性
mask屬性之前從來沒有使用過,感覺很陌生,所以特意找了一下這方面的。使用CALayer的Mask實現(xiàn)注水動畫效果這個效果很不錯,直接拿過來了
mask實際上是layer內(nèi)容的一個遮罩,如果我們把mask設(shè)置成透明的,實際看到的layer是完全透明的(這個layer就看不到了),也就是說只有mask的內(nèi)容不透明的部分和layer疊加的部分才會顯示出來,效果如下:

一張圖片說明了一切,代碼就不貼了,直接參考原作者的吧。
設(shè)計的思路參考《基于Core Animation的KTV歌詞視圖的平滑實現(xiàn)》,Facebook Shimmer。

CALayer的動畫 -- Core Animation
這個內(nèi)容就太多了,動畫部分不多說,注意:核心動畫作用在layer上。
貼幾篇動畫相關(guān)文章
iOS動畫-從UIView動畫說起
iOS動畫-Transform和KeyFrame動畫
iOS動畫-layout動畫初體驗
iOS動畫-layout動畫的更多使用
iOS動畫-碎片動畫
iOS動畫-認識CoreAnimation
3. CAShapLayer
- CAShapeLayer繼承自CALayer,可使用CALayer的所有屬性
- CAShapeLayer需要和貝塞爾曲線配合使用才有意義。
在蘋果的General Tips and Tricks可以找到這句話
The CAShapeLayer class creates its content by rendering the path you provide into a bitmap image at composite time.
- 使用CAShapeLayer與貝塞爾曲線可以實現(xiàn)不在view的DrawRect方法中畫出一些想要的圖形
The CAShapeLayer class draws a cubic Bezier spline in its coordinate space.
//CAShapeLayer這個類是用來繪制三次貝塞爾曲線的
CAShapeLayer結(jié)合貝塞爾曲線(UIBezierPath)**
UIBezierPath在Quartz2D部分有介紹,CGPathRef是基于C語言的路徑,UIBezierPath是OC版的,用法很類似。他們都是用來繪制曲線的
繪制的方法
+ (instancetype)bezierPath;
+ (instancetype)bezierPathWithRect:(CGRect)rect;
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
- (void)moveToPoint:(CGPoint)point;
- (void)addLineToPoint:(CGPoint)point;
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
示例代碼:
1. 自定義圓形進度條(CAShapLayer實現(xiàn)):
CAShapLayer中有兩個非常關(guān)鍵的屬性strokeEnd,strokeStart。自定義圓形進度條(drawRect實現(xiàn))中,我們是通過調(diào)用[self setNeedDisplay]方法來實現(xiàn)視圖的繪制的,但是在CAShapLayer中,直接設(shè)置strokeEnd就可以直接自動的去繪制視圖更新,不需要我們操作什么,就像設(shè)置view的frame,bounds等一樣,系統(tǒng)自動幫我們做了。

//
// CustomProgressLayer.m
// CAShapeLayer實現(xiàn)圓形進度條
//
// Created by yangguangyu on 16/8/31.
// Copyright ? 2016年 yangguangyu. All rights reserved.
//
#import "CustomProgressLayer.h"
@implementation CustomProgressLayer
- (instancetype)init
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
-(void)setup {
self.strokeColor = [UIColor redColor].CGColor;
self.fillColor = [UIColor clearColor].CGColor;
self.lineWidth = 5;
self.strokeStart = 0;
self.strokeEnd = 0;
}
-(void)addProgressLayerOnView:(UIView *)view {
self.frame = view.bounds;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5) radius:self.frame.size.width * 0.1 startAngle:0 endAngle:2 * M_PI clockwise:YES];
self.path = path.CGPath;
[view.layer addSublayer:self];
}
-(void)setProgressValue:(CGFloat)progressValue {
_progressValue = progressValue;
if (progressValue < 0) {
progressValue = 0;
}else if (progressValue > 1) {
progressValue = 1;
}
self.strokeEnd = progressValue;
}
@end
在www.raywenderlich.com官網(wǎng)上,有一個非常不錯的圓形圖片加載動畫

前面我們已經(jīng)實現(xiàn)了圓環(huán)的效果,只需要在圖片加載完成的時候添加一個動畫
-(void)reveal {
self.superlayer.mask = self;
//動畫
CABasicAnimation *lineAnimation = [CABasicAnimation animationWithKeyPath:@"lineWidth"];
// lineAnimation.toValue = @(300);//這里決定了中間最后狀態(tài)時中心圓環(huán)的大小
lineAnimation.toValue = @(self.bounds.size.width);
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
UIBezierPath *toPath = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
pathAnimation.toValue = toPath;//這里決定了最外層
CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
group.animations = @[lineAnimation,pathAnimation];
group.duration = 2;
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;
[self addAnimation:group forKey:nil];
}
原版的是swift的,原貼How To Implement A Circular Image Loader Animation with CAShapeLayer,自己用OC也寫了一份demo
2. 繪制虛線(不用CAShaprLayer也可以實現(xiàn),直接使用Quartz2D)
/**
** lineView: 需要繪制成虛線的view
** lineLength: 虛線的寬度
** lineSpacing: 虛線的間距
** lineColor: 虛線的顏色
**/
+ (void)drawDashLine:(UIView *)lineView lineLength:(int)lineLength lineSpacing:(int)lineSpacing lineColor:(UIColor *)lineColor
{
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setBounds:lineView.bounds];
[shapeLayer setPosition:CGPointMake(CGRectGetWidth(lineView.frame) / 2, CGRectGetHeight(lineView.frame))];
[shapeLayer setFillColor:[UIColor clearColor].CGColor];
// 設(shè)置虛線顏色為blackColor
[shapeLayer setStrokeColor:lineColor.CGColor];
// 設(shè)置虛線寬度
[shapeLayer setLineWidth:CGRectGetHeight(lineView.frame)];
[shapeLayer setLineJoin:kCALineJoinRound];
// 設(shè)置線寬,線間距
[shapeLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:lineLength], [NSNumber numberWithInt:lineSpacing], nil]];
// 設(shè)置路徑
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, CGRectGetWidth(lineView.frame), 0);
[shapeLayer setPath:path];
CGPathRelease(path);
// 把繪制好的虛線添加上來
[lineView.layer addSublayer:shapeLayer];
}
3. 給UIView添加分隔線
在很多情況下,我們需要給UIView添加一個分隔線,尤其是使用UITableView的時候,經(jīng)常會自己搞分割線,以前我的做法是在cell的底部添加一個高度為1的UIView,使用CAShapeLayer或者CALayer可以不用再添加view了,直接搞一個layer上去。寫一個UIView的分類
//
// UIView+BorderLine.m
// CAShapeLayer分割線
//
// Created by yangguangyu on 16/9/2.
// Copyright ? 2016年 yangguangyu. All rights reserved.
//
#import "UIView+BorderLine.h"
@implementation UIView (BorderLine)
-(void)addBorderTopLine:(CGSize)size andColor:(UIColor *)color {
[self addBorderLineWithframe:CGRectMake(0, 0 , size.width, size.height) andColor:color];
}
-(void)addBorderLeftLine:(CGSize)size andColor:(UIColor *)color {
[self addBorderLineWithframe:CGRectMake(0, 0, size.width, size.height) andColor:color];
}
-(void)addBorderBottomLine:(CGSize)size andColor:(UIColor *)color {
[self addBorderLineWithframe:CGRectMake(0, self.frame.size.height - size.height, size.width, size.height) andColor:color];
}
-(void)addBorderRightLine:(CGSize)size andColor:(UIColor *)color {
[self addBorderLineWithframe:CGRectMake(self.frame.size.width - size.width, 0, size.width, size.height) andColor:color];
}
#pragma mark - padding
-(void)addBorderTopLine:(CGSize)size andColor:(UIColor *)color andPadding:(CGFloat)padding {
[self addBorderLineWithframe:CGRectMake(padding, 0 , size.width - 2 * padding, size.height) andColor:color];
}
-(void)addBorderLeftLine:(CGSize)size andColor:(UIColor *)color andPadding:(CGFloat)padding {
[self addBorderLineWithframe:CGRectMake(0, padding, size.width, size.height - 2 * padding) andColor:color];
}
-(void)addBorderBottomLine:(CGSize)size andColor:(UIColor *)color andPadding:(CGFloat)padding {
[self addBorderLineWithframe:CGRectMake(padding, self.frame.size.height - size.height, size.width - 2 * padding, size.height) andColor:color];
}
-(void)addBorderRightLine:(CGSize)size andColor:(UIColor *)color andPadding:(CGFloat)padding {
[self addBorderLineWithframe:CGRectMake(self.frame.size.width - size.width, padding, size.width, size.height - 2 * padding) andColor:color];
}
//-------
-(void)addBorderLineWithframe:(CGRect)frame andColor:(UIColor *)color {
CAShapeLayer *layer = [CAShapeLayer layer];
layer.frame = frame;
layer.backgroundColor = color.CGColor;
[self.layer addSublayer:layer];
}
@end
參考文章:
OS開發(fā)UI篇—Quartz2D簡單介紹
使用CALayer的Mask實現(xiàn)注水動畫效果
iOS開發(fā)之讓你的應用“動”起來崔江濤的教程都很好,講的非常全面
How To Implement A Circular Image Loader Animation with CAShapeLayer
放肆的使用UIBezierPath和CAShapeLayer畫各種圖形
關(guān)于CAShapeLayer的一些實用案例和技巧
基于CAShapeLayer和貝塞爾曲線的圓形進度條動畫【原創(chuàng)】