iOS 2D繪圖詳解(Quartz 2D)之概述 – iPhone手機開發 iPhone軟體開發教學課程

 


前言:最近在研究自定義控件,由於想要徹底的定制控件的視圖還是要繼承UIView,雖然對CALayer及其子類很熟練,但是對Quartz 2D這個強大的框架仍然概念模棱兩可。於是,決定學習下,暫定7篇文章講解,會寫一些Demo。
官方文檔

本文的代碼Demo在最後一部分


Quartz 2D用來幹嘛的?

Quartz 2D屬於Core Graphics(所以大多數相關方法的都是以CG開頭),是iOS/Mac OSX 提供的在內核之上的強大的2D繪圖引擎,並且這個繪圖引擎是設備無關的。也就是說,不用關心設備的大小,設備的分辨率,隻要利用Quartz 2D,這些設備相關的會自動處理。Quartz 2D能夠提供的強大功能如下

透明層(transparency layers) 陰影 基於path的繪圖(path-based drawing) 離屏渲染(offscreen rendering) 復雜的顏色處理(advanced color management) 抗鋸齒渲染(anti-aliased rendering) PDF創建,展示,解析(這部分不在這個系列之中) 配合Core Animation, OpenGL ES,UIKit完成復雜的功能


畫板-The Graphics Context

既然提到繪圖,那自然有一個容器來包含繪制的結果,然後把這個結果渲染到屏幕上去,而Quartz 2D的容器就是CGContextRef數據模型。這種數據模型是C的結構體,存儲瞭渲染到屏幕上需要的一切信息。
那麼,最後 Graphics Context 可以渲染到哪裡呢?

Layer Window 打印機 PDF Bitmap(圖片)


繪制模型

Quartz 2D采用painter’s model,意味著每一次繪制都是一層,然後按照順序一層層的疊加到畫板上。例如


數據類型

Quartz 2D中的數據類型都是透明的,也就是說用戶隻需要使用即可,不需要實際訪問其中的變量。具體的數據類型包括

CGPathRef 路徑類型,用來繪制路徑(註意帶有ref後綴的一般都是繪制的畫板) CGImageRef,繪制bitmap CGLayerRef,繪制layer,layer可復用,可離屏渲染 CGPatternRef,重復繪制 CGShadingRef和CGGradientRef,繪制漸變(例如顏色漸變) CGFunctionRef,定義回調函數,CGShadingRef和CGGradientRef的輔助類型 CGColorRef and CGColorSpaceRef,定義如何處理顏色 CGFontRef,繪制文字 其他類型(pdf這個系列不講解,所以不會涉及)


繪制狀態

在使用quartz 2d進行繪圖的時候,經常需要設置顏色,字體,設置context的坐標原點變換,context旋轉。這些影響的都是當前繪制狀態。Context中利用堆棧的方式來保存繪制狀態。調用CGContextSaveGState來保存當前繪制狀態的copy到堆棧中,利用CGContextRestoreGState彈出堆棧最頂層的繪制狀態,設置為當前的繪制狀態。註意,不是所有的參數都會保存,以下表格中的參數會保存


坐標系

和UIKit的坐標系不一樣,Quartz 2D的坐標系是在左下角的。

Quartz利用坐標系的旋轉位移等操作來繪制復雜的動畫。
但是有兩個地方的坐標系是正常的UIKit坐標系

UIView 的context 通過這個方法UIGraphicsBeginImageContextWithOptions返回的context


一個簡單的Demo講解

新建一個UIView的子類,然後重寫drawRect

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
    CGContextFillRect(context, rect);//先填充整個區域
    CGRect testRect = CGRectMake(10, 10, 20, 20);
    CGContextAddRect(context, testRect);
    CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);//修改畫筆顏色
    CGContextFillRect(context, testRect);//填充部分區域
}

然後,這樣調用

    LeoDemoView * demoView = [[LeoDemoView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [self.view addSubview:demoView];

效果
這裡寫圖片描述
可以看到坐標系是正常的UIKit坐標系

然後,我們在在上面繪制一個文字”Leo”,在上述drawRect的最後添加,vcD4KPHByZSBjbGFzcz0=”brush:java;”>
NSString * toDraw = @Leo;
[toDraw drawAtPoint:CGPointMake(CGRectGetWidth(rect)/2, CGRectGetHeight(rect)/2) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12],NSForegroundColorAttributeName:[UIColor greenColor]}];

可以看到,繪制是一層一層覆蓋的,

最後,我們在右下角繪制一個紅心,但是希望紅心是反過來的。這裡用到瞭上文的所說的繪制狀態堆棧。繼續在drawRect的最後添加如下代碼

    CGContextSaveGState(context);
    CGContextTranslateCTM(context,rect.size.width,rect.size.height);
    CGContextRotateCTM(context, M_PI);
    NSString * redHeart = @??;//MarkDown 顯示不出紅心
    [redHeart drawAtPoint:CGPointMake(0, 0) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]}];

    CGContextRestoreGState(context);
}

效果

這裡,初學者可能不懂到底是怎麼回事瞭。我通過圖解的方式一步步解釋

這行代碼把坐標系移動到右下角
CGContextTranslateCTM(context,rect.size.width,rect.size.height);
如圖

接著把坐標系逆時針旋轉180度

    CGContextRotateCTM(context, M_PI);

這時候的坐標系

這時候,參考這個坐標系進行繪制,看到的就是反過來的。哇咔咔!


 

發佈留言