概述
iOS主要的繪圖系統有UIKit,Core Graphics,Core Animation,Core Image,Open GL等,本片博文主要介紹UIKit與Core Graphics
的繪圖系統。
關於UIKit
iOS中的原生控件以UI前綴
開頭的類名都是由UIKit
繪製的。可以說我們進行開發打交道最多的就是UIKit
這個框架。
關於Core Graphics
Core Graphics
是iOS主要的繪圖系統,可以在屏幕,圖層,位圖,PDF或者打印機上繪製。在iOS中以CG前綴
的類都屬於Core Graphics
框架。
UIKit繪圖系統
視圖繪製
- 視圖繪製的週期
所有繪製的視圖都發生在主線程,如果在主線程進行耗時操作的話會阻礙繪製的更新,不能把主視圖的繪圖操作放到其他線程中,這對於當前的UIKit
是線程不安全的。如果在其他線程對主視圖進行繪製會導致繪製出錯或崩潰。 - 視圖繪製的方法
setNeedsDispaly
如果對視圖調用setNeedsDispaly
方法,它會標記成爲需要刷新
並且在下一繪圖週期中重新繪製,不過大部分UIKit視圖會在數據發生變化時自動進行重繪操作,因此除非想要在視圖上自定義繪圖,其他情況並不需要調用setNeedsDispaly
方法。
通過UIKit繪圖
UIKit
通過UIRectFrame
和UIRectFill
可以進行一些簡單的繪製矩形的方法,如果想要繪製任意圖形需要用到UIBezierPath
進行繪製,但是UIKit
對一些高級的特性依然無能爲力比如陰影、漸變等效果。UIBezierPath
可以繪製任意的曲線和線條,因爲UIBezierPath
擁有處理大部分線條、弧線、矩形、橢圓的簡單方法,因此UIBezierPath
可以快速繪製大部分形狀。- 繪圖在系統提供的圖形上下文中完成後會調用
drawRect:
方法,所以我們自定義繪圖操作需要寫在drawRect:
方法裏。
UIRectFrame與UIRectFill簡單的實現
- (void)drawRect:(CGRect)rect {
[[UIColor greenColor] setFill]; //設置填充顏色
UIRectFill(rect); //設置填充區域
[[UIColor redColor] setStroke]; //設置線條顏色
UIRectFrame(CGRectMake(10, 10, 50, 50)); //設置矩形區域
}
實現效果
UIBezierPath的實現
- (void)drawRect:(CGRect)rect {
CGSize size = rect.size;
[[UIColor grayColor] setFill];
UIRectFill(rect); //設置一個背景色
CGFloat margin = 10; //圖像距離矩形上下邊框寬度
CGFloat radius = rintf(MIN(size.height-margin, size.width-margin)/4);
//確保圓弧能夠完整的畫在矩形框內,以矩形框最小的邊長1/4畫爲半徑畫圓弧
CGFloat xOffset,yOffset;
CGFloat offset = rintf((size.height - size.width)/2);
if (offset>0) {
xOffset = rint(margin/2);
yOffset = offset;
}
else
{
xOffset = -offset;
yOffset = rint(margin/2);
}
[[UIColor redColor] setFill];
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:CGPointMake(radius * 2+xOffset, radius+yOffset) //圓弧的中心
radius:radius //圓弧的角度
startAngle:-M_PI //開始的角度
endAngle:0 //結束的角度
clockwise:YES]; //畫圓弧的方向(YES--順時針,NO--逆時針)
[path addArcWithCenter:CGPointMake(radius * 3+xOffset, radius*2+yOffset)
radius:radius
startAngle:-M_PI_2
endAngle:M_PI_2
clockwise:YES];
[path addArcWithCenter:CGPointMake(radius * 2+xOffset, radius*3+yOffset)
radius:radius
startAngle:0
endAngle:M_PI
clockwise:YES];
[path addArcWithCenter:CGPointMake(radius +xOffset, radius*2+yOffset)
radius:radius
startAngle:M_PI_2
endAngle:-M_PI_2
clockwise:YES];
[path closePath];
[path fill];
}
實現效果
Core Graphics繪圖系統
Core Graphics
通過CGPath
繪製簡單的實現
-(void)drawRect:(CGRect)rect
{
[[UIColor grayColor] setFill];
UIRectFill(rect); //設置背景色
CGContextRef ctx = UIGraphicsGetCurrentContext(); //獲取當前上下文
CGContextSetStrokeColorWithColor(ctx, [[UIColor redColor] CGColor]); //設置線條顏色
CGContextSetLineJoin(ctx, kCGLineJoinRound); //設置兩條線條連接點樣式
CGContextSetLineWidth(ctx, 5); //設置線條寬度
CGMutablePathRef path = CGPathCreateMutable(); //創建路徑
CGPathMoveToPoint(path, nil, 10, 10); //設置路徑起始點
CGPathAddLineToPoint(path, nil, 100, 100); //移動到指定的Point
CGPathAddLineToPoint(path, nil, 200, 10);
CGContextAddPath(ctx, path); //添加路徑到當前上下文
CGPathRelease(path); //釋放路徑
CGContextStrokePath(ctx); // 繪製當前上下文
}
實現效果
注意
Core Graphics
屬於Core Fundation
框架,Core Fundation
不能ARC管理內存。所以Core Foundation
對象需要手動釋放,即便啓用了ARC
。
iOS繪圖Tip
關於Core Graphics座標
在drawRect:
方法中,Core Graphics
繪製的東西是上下顛倒的,我們正常是以左上角座標原點,而Core Graphics
默認的是以左下角爲座標原點。如果想讓Core Graphics
繪製的圖像以我們常用的左上角座標原點,只要在drawRect:
方法中通過UIGraphicsGetCurrentContext()
返回的上下文一切就正常了。如果使用自己創建的上下文,發現座標是以左下角則需要進行以下操作:
CGContextTranslateCTM(ctx, 0,rect.size.height); //rect.size.height --- 當前view的高度
CGContextScaleCTM(ctx,1,-1); //x軸座標不變,y軸座標取反
管理圖形上下文
CGContextSaveGState
繪圖系統在調用drawRect:
方法時創建的圖形上下文中包括大量信息,繪圖的顏色,線條的寬度,字體大小等。當此時某一時刻想更改這些信息,但是過後還想恢復之前的狀態就需要用到CGContextSaveGState
來保存之前的狀態。CGContextRestoreGState
用來恢復之前由CGContextSaveGState
保存的狀態UIGraphicsPushContext
UIKit繪製時只會對棧頂的context進行操作,所以當要繪製一個上下文時要把這個上下文push
到管理上下文堆棧的棧頂。UIGraphicsPopContext
當繪製一個上下時要進行Push
後才能操作,當繪製結束時需要Pop
出棧。UIGraphicsBeginImageContext
UIGraphicsBeginImageContext
是對UIGraphicsPushContext
和UIGraphicsPopContext
操作的封裝,負責將舊的上下文入棧,爲新的上下文分配內存,創建新的上下文,翻轉座標系,並作爲當前上下文使用。
CGContextSaveGState與CGContextRestoreGState簡單實現
-(void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); //設置繪製顏色
CGContextSetLineWidth(ctx, 10); //設置線條寬度
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 10);
CGContextStrokePath(ctx);
CGContextSaveGState(ctx); //保存當前上下文狀態
CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor); //設置繪製顏色
CGContextSetLineWidth(ctx, 5); //設置線條寬度
CGContextMoveToPoint(ctx, 10, 30);
CGContextAddLineToPoint(ctx, 100, 30);
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx); //取出之前保存的上下文狀態
CGContextMoveToPoint(ctx, 10, 50);
CGContextAddLineToPoint(ctx, 100, 50);
CGContextStrokePath(ctx);
}
實現效果
UIGraphicsBeginImageContext使用
- (void)viewDidLoad {
[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [self creatImage];
[self.view addSubview:imageView];
}
-(UIImage *)creatImage
{
const CGFloat kImageWidth = 100;
const CGFloat kImageHeight = 100; //設置圖片尺寸
NSDictionary *attributeDic = @{NSFontAttributeName:[UIFont systemFontOfSize:14],NSForegroundColorAttributeName:[UIColor redColor]}; //設置字體屬性
UIGraphicsBeginImageContext(CGSizeMake(kImageWidth, kImageWidth)); //創建上下文
[@"GGGHub" drawInRect:CGRectMake(0, 0, kImageWidth, kImageHeight)
withAttributes:attributeDic]; //把文字繪製到當前上下文
CGImageRef textImage = UIGraphicsGetImageFromCurrentImageContext().CGImage;
UIGraphicsEndImageContext(); //繪製結束
return [UIImage imageWithCGImage:textImage];
}
實現效果
本篇博文參考