Swift之你應該懂點Core Graphics (長篇高能)

Core Graphics是基于C的API,可以用于一切繪圖

Quartz 2D是Core Graphics框架的一部分,是一個強大的二維圖像繪制引擎。Quartz 2D在UIKit中也有很好的封裝和集成,我們日常開發時所用到的UIKit中的組件都是由Core Graphics進行繪制的。不僅如此,當我們引入UIKit框架時系統會自動引入Core Graphics框架,并且為了方便開發者使用在UIKit內部還對一些常用的繪圖API進行了封裝。

不廢話,直接來看利用Core Graphics怎么繪圖。

一般步驟

  • 1、獲取繪圖上下文
  • 2、創建并設置路徑
  • 3、將路徑添加到上下文
  • 4、設置上下文狀態
  • 5、繪制路徑

圖形上下文CGContextRef代表圖形輸出設備(也就是繪制的位置),包含了繪制圖形的一些設備信息。Quartz 2D中所有對象都必須繪制在圖形上下文

基本圖形的繪制

希望大家打開Xcode跟著練習

創建一個Single View Application ,新建一個繼承自View的子類CGView

import UIKit

class CGView: UIView {

    
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        // 1、 獲取上下文對象
        let context = UIGraphicsGetCurrentContext()
        
        // 2、 創建路徑對象
        let path = CGPathCreateMutable()
        CGPathMoveToPoint(path, nil, 10,30)
        CGPathAddLineToPoint(path, nil, 10, 100)
        CGPathAddLineToPoint(path, nil, 150, 100)
        
        // 3、 添加路徑到圖形上下文 
        CGContextAddPath(context, path)
        
        // 4、 設置圖形上下文狀態屬性
        CGContextSetRGBStrokeColor(context, 1, 0, 1, 1) //設置筆觸顏色
        CGContextSetRGBFillColor(context, 1, 1, 0, 1)   //設置填充色
        
        CGContextSetLineWidth(context, 5)   //設置線條寬度
        CGContextSetLineCap(context, CGLineCap.Round ) // 設置頂點樣式
        CGContextSetLineJoin(context, CGLineJoin.Round) //設置連接點樣式
        /*設置線段樣式
        phase:虛線開始的位置  這里設置為0
        lengths:虛線長度間隔
        count:虛線數組元素個數
        */
        let lengths: [CGFloat] = [5,7] //長度間隔
        CGContextSetLineDash(context, 0 , lengths, 2)
        
        let color = UIColor.grayColor().CGColor //顏色轉化,由于Quartz 2D跨平臺,所以其中不能使用UIKit中的對象,但是UIkit提供了轉化方法
        /*設置陰影
        context:圖形上下文
        offset:偏移量
        blur:模糊度
        color:陰影顏色
        */
        CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 0.8, color)
        
        // 5、 繪制圖像到指定圖形上下文
        /*
        CGPathDrawingMode是填充方式,枚舉類型
        Fill:只有填充(非零纏繞數填充),不繪制邊框
        EOFill:奇偶規則填充(多條路徑交叉時,奇數交叉填充,偶交叉不填充)
        Stroke:只有邊框
        FillStroke:既有邊框又有填充
        EOFillStroke:奇偶填充并繪制邊框
        */
        CGContextDrawPath(context, CGPathDrawingMode.FillStroke) //最后一個參數是填充類型
    }
}

UIKit已經為我們準備好了一個上下文,在drawRect:方法(這個方法在loadView、viewDidLoad方法后執行)中我們可以通過UIKit封裝函數UIGraphicsGetCurrentContext()方法獲得這個圖形上下文

然后我們按照上面說的步驟一步一步的執行下來得到結果:

然后在vc的viewDidLoad中加入代碼

  let cgView = CGView(frame:CGRectMake(0,44,160,110) )
  cgView.backgroundColor = UIColor.whiteColor()
  self.view.addSubview(cgView)

得到結果如圖:

配圖

上面的步驟略顯復雜,其實代碼還可以簡化

我們再創建一個繼承自UIView的子類 CGView2

import UIKit

class CGView2: UIView {

    
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        
        // 1、 獲取上下文對象
        let context = UIGraphicsGetCurrentContext()
        
        // 2、 繪制路徑(相當于前面創建路徑并添加路徑到圖形上下文兩步操作)
        CGContextMoveToPoint(context, 10, 30)
        CGContextAddLineToPoint(context, 10, 100)
        CGContextAddLineToPoint(context, 150, 100)

        //封閉路徑:直接調用路徑封閉方法
        CGContextClosePath(context)
        
        // 3、 設置圖形上下文屬性
        UIColor.redColor().setStroke()
        UIColor.yellowColor().setFill()
        
        // 4、 繪制路徑
        CGContextDrawPath(context, CGPathDrawingMode.FillStroke)
        
    }
    
}

上面代碼精簡了許多,Core Graphics 內部對創建對象添加到上下文這兩步操作進行了封裝,可以一步完成

繼續在剛的vc的viewDidLoad:中加入代碼

let cgView = CGView(frame:CGRectMake(0,44,160,110) )
cgView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(cgView)
        
let cgView2 = CGView2(frame:CGRectMake(160,44,160,110))
cgView2.backgroundColor = UIColor.whiteColor()
self.view.addSubview(cgView2)

得到結果:

配圖
繪制矩形

我們再來創建一個繼承自 UIView的子類CGView3

 /**
     畫矩形
     
     - parameter context: 上下文對象
     */
    func drawRectWithContext(context:CGContextRef){
        let rect = CGRectMake(20, 0 , 280, 50)
        CGContextAddRect(context, rect)
        UIColor.blueColor().set()
        CGContextDrawPath(context, CGPathDrawingMode.FillStroke)
    }

然后在drawrect中調用此方法

 override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        
        // 1、 獲取上下文對象
        let context = UIGraphicsGetCurrentContext()
        //畫矩形
        self.drawRectWithContext(context!)
    }

在vc對應viewDidLoad中加入此View

  override func viewDidLoad() {
        super.viewDidLoad()
        title = "CoreGraphics Demo"
        
        // Do any additional setup after loading the view, typically from a nib.
        let cgView = CGView(frame:CGRectMake(0,44,160,110) )
        cgView.backgroundColor = UIColor.whiteColor()
        self.view.addSubview(cgView)
        
        let cgView2 = CGView2(frame:CGRectMake(160,44,160,110))
        cgView2.backgroundColor = UIColor.whiteColor()
        self.view.addSubview(cgView2)
        
        let cgView3 = CGView3(frame:CGRectMake(0,164,self.view.frame.size.width,self.view.frame.size.height - 110))
        cgView3.backgroundColor = UIColor.whiteColor()
        self.view.addSubview(cgView3)
        
    }

運行后結果:

配圖

其實UIKit對繪圖方法的封裝,使用起來更加簡單。我們可以直接使用
直接在CGView3中繼續添加方法

 /**
     畫矩形 用uikit封裝的方法
     
     - parameter context: 上下文對象
     */
    func drawRectByUIKitWithContext(context:CGContextRef){
        let rect = CGRectMake(20, 60, 280.0, 50.0)
        let rect2 = CGRectMake(20, 120, 280.0, 50.0)
        
        UIColor.yellowColor().set()
        UIRectFill(rect)
        
        UIColor.redColor().setFill()
        UIRectFill(rect2)
    }

然后在drawRect中調用 運行結果

配圖
繪制橢圓和圓

繼續在CGView3中添加方法

/**
     畫橢圓 寬高一樣就是正圓
     
     - parameter context: 上下文對象
     */
    func drawEllipse(context:CGContextRef){
        let rect = CGRectMake(20, 180 ,100,120)
        
        CGContextAddEllipseInRect(context, rect)
        UIColor.purpleColor().set()
        CGContextDrawPath(context, CGPathDrawingMode.Fill)
    }

這里寬高設置的不一樣 只要把寬和高設置一樣就是正圓

在drawRect中添加此方法

運行結果:

配圖
繪制弧形
 /**
     創建弧形
     
     - parameter context: 上下文對象
     */
    func drawArc(context:CGContextRef){
        /*
        添加弧形對象
        x:中心點x坐標
        y:中心點y坐標
        radius:半徑
        startAngle:起始弧度
        endAngle:終止弧度
        closewise:是否逆時針繪制,0則順時針繪制
        */
        CGContextAddArc(context, 200, 250 , 50 , 0 , CGFloat(M_PI) , 0)
        UIColor.greenColor().set()
        CGContextDrawPath(context, CGPathDrawingMode.Fill)
    }

和上面一樣 添加到drawRect

運行結果

配圖
繪制貝塞爾曲線

在Quartz 2D中曲線繪制分為兩種:二次貝塞爾曲線和三次貝塞爾曲線。二次曲線只有一個控制點,而三次曲線有兩個控制點,如下圖所示:

配圖

下面演示下繪制方法

  /**
     繪制貝塞爾曲線
     
     - parameter context: 上下文對象
     */
    func drawCurve(context:CGContextRef){
        //繪制曲線
        CGContextMoveToPoint(context, 20, 310) //移動到起始位置
        /*
        繪制二次貝塞爾曲線
        c:圖形上下文
        cpx:控制點x坐標
        cpy:控制點y坐標
        x:結束點x坐標
        y:結束點y坐標
        */
        CGContextAddQuadCurveToPoint(context, 220, 310 , 100, 400)
        
        CGContextMoveToPoint(context, 230, 310)
        /*繪制三次貝塞爾曲線
        c:圖形上下文
        cp1x:第一個控制點x坐標
        cp1y:第一個控制點y坐標
        cp2x:第二個控制點x坐標
        cp2y:第二個控制點y坐標
        x:結束點x坐標
        y:結束點y坐標
        */
        CGContextAddCurveToPoint(context, 100 , 330, 400, 410, 300, 500);
        
        UIColor.yellowColor().setFill()
        UIColor.redColor().setStroke()
        CGContextDrawPath(context, CGPathDrawingMode.FillStroke)
    }
    

上面所有方法加入到drawRect代碼如下:

  override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        
        // 1、 獲取上下文對象
        let context = UIGraphicsGetCurrentContext()
        //畫矩形
        self.drawRectWithContext(context!)
        //畫矩形 用uikit封裝的方法
        self.drawRectByUIKitWithContext(context!)
        //畫橢圓 寬高一樣就是正圓
        self.drawEllipse(context!)
        //畫弧形
        self.drawArc(context!)
        //繪制 二次三次貝塞爾曲線
        self.drawCurve(context!)
    }

結果:

配圖
文字繪制

Core Graphics不僅可以畫圖還能繪制文字
這個頁面已經放不下了 開一個新的頁面 繼續,后面我會把所有代碼上傳github 供大家參考

創建一個新的繼承自UIView的類CGView4

添加下面方法

 /**
     文字繪制
     
     - parameter context: 上下文對象
     */
    func drawText(context:CGContextRef){
        let str = "使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制使用CoreGraphics進行文字繪制"
        let rect = CGRectMake(20, 20, 280, 200)
        let font = UIFont.systemFontOfSize(16)
        let color = UIColor.redColor()
        let style = NSMutableParagraphStyle()
        style.alignment = NSTextAlignment.Left
        (str as NSString).drawInRect(rect, withAttributes: [NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style])
        
    }

聲明了一個字符串,設置了繪制區間、字體顏色段落屬性等 當然還可以設置更多其他屬性,設置屬性參考我的另一篇文章Swift之CoreText排版神器(長篇高能)

畫好之后 還在在drawRect中調用

 override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        
        let context = UIGraphicsGetCurrentContext()
        
        //文字繪制
        self.drawText(context!)

    }

加入新的vc的viewDidLoad方法

    
    override func viewDidLoad() {
        super.viewDidLoad()
        let cgView4 = CGView4(frame:CGRectMake(0,64,self.view.frame.width,self.view.frame.height))
        cgView4.backgroundColor = UIColor.whiteColor()
        self.view.addSubview(cgView4)
        
    }

結果

配圖

文字已經正確的繪制到視圖上

圖像繪制

當然Core Graphics也能繪制圖像

很簡單

  /**
     圖像繪制
     
     - parameter context: 上下文對象
     */
    func drawImage(context:CGContextRef){
    
        let img = UIImage(named: "ddla")
        //繪制到指定的矩形中,注意如果大小不合適會會進行拉伸
        img?.drawInRect(CGRectMake(0, 200, 100, 80))
        //從某一點開始繪制
        img?.drawAtPoint(CGPoint(x: 0, y: 320))
        
    }

可以從某點開始繪制也可以指定位置區域

配圖
繪制漸變

Quartz 2D的漸變方式分為兩種:

  • 線性漸變線:漸變色以直線方式從開始位置逐漸向結束位置漸變
  • 徑向漸變:以中心點為圓心從起始漸變色向四周輻射,直到終止漸變色

在iOS中繪制漸變還需要注意一點就是指定顏色空間,所謂顏色空間就是不同顏色在不同的維度上取值最終組成一種顏色的過程。就拿RGB來說,如果將紅色、綠色、藍色看成是x、y、z軸坐標系,那么在三個坐標上分別取0~255范圍內的不同值則可以組成各類顏色。當然,不同顏色空間的“坐標系”也是不同的(也就是說顏色表示的方式是不同的),常用的顏色空間除了RGB還有CMYK(印刷業常用這種顏色模式)、Gray

下面的代碼分別演示了兩種漸變方式,具體漸變繪制函數參數代碼中已經注釋的很清楚了:

線性漸變

新建一個CGView5

class CGView5: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        let context = UIGraphicsGetCurrentContext()
        
        //繪制線性漸變
        self.drawLinearGradient(context!)
        
    }
    
    /**
     繪制線性漸變
     
     - parameter context: 上下文
     */
    func drawLinearGradient(context:CGContextRef){
        
        //使用rgb顏色空間
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        /*
        指定漸變色
        space:顏色空間
        components:顏色數組,注意由于指定了RGB顏色空間,那么四個數組元素表示一個顏色(red、green、blue、alpha),
        如果有三個顏色則這個數組有4*3個元素
        locations:顏色所在位置(范圍0~1),這個數組的個數不小于components中存放顏色的個數
        count:漸變個數,等于locations的個數
        */
        let compoents:[CGFloat] = [ 248.0/255.0,86.0/255.0,86.0/255.0,1,
            249.0/255.0,127.0/255.0,127.0/255.0,1,
            1.0,1.0,1.0,1.0]
        
        let locations:[CGFloat] = [0,0.4,1]
        let gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, locations.count)
        /*
        繪制線性漸變
        context:圖形上下文
        gradient:漸變色
        startPoint:起始位置
        endPoint:終止位置
        options:繪制方式,DrawsBeforeStartLocation 開始位置之前就進行繪制,到結束位置之后不再繪制,
        DrawsAfterEndLocation開始位置之前不進行繪制,到結束點之后繼續填充
        */
        CGContextDrawLinearGradient(context, gradient , CGPointZero, CGPointMake(self.frame.size.width,100), CGGradientDrawingOptions.DrawsAfterEndLocation)
    }
    
}

然后添加到新的vc中

  let ctView = CGView5(frame:CGRectMake(0,64,self.view.frame.width,100))
  ctView.backgroundColor = UIColor.whiteColor()
 self.view.addSubview(ctView)

效果

配圖
徑向漸變

新建一個CGView6

  /**
     徑向漸變繪制
     
     - parameter context: 上下文
     */
    func drawRadialGradient(context:CGContextRef){
        
        //使用rgb顏色空間
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        /*
        指定漸變色
        space:顏色空間
        components:顏色數組,注意由于指定了RGB顏色空間,那么四個數組元素表示一個顏色(red、green、blue、alpha),
        如果有三個顏色則這個數組有4*3個元素
        locations:顏色所在位置(范圍0~1),這個數組的個數不小于components中存放顏色的個數
        count:漸變個數,等于locations的個數
        */
        let compoents:[CGFloat] = [ 248.0/255.0,86.0/255.0,86.0/255.0,1,
            249.0/255.0,127.0/255.0,127.0/255.0,1,
            1.0,1.0,1.0,1.0]
        
        let locations:[CGFloat] = [0,0.4,1]
        let gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, locations.count)
        /*
        繪制線性漸變
        context:圖形上下文
        gradient:漸變色
        startPoint:起始位置
        startRadius:起始半徑(通常為0,否則在此半徑范圍內容無任何填充)
        endCenter:終點位置(通常和起始點相同,否則會有偏移)
        endRadius:終點半徑(也就是漸變的擴散長度)
        options:繪制方式,DrawsBeforeStartLocation 開始位置之前就進行繪制,到結束位置之后不再繪制,
        DrawsAfterEndLocation開始位置之前不進行繪制,到結束點之后繼續填充
        */
        CGContextDrawRadialGradient(context, gradient, CGPointMake(100,100), 0, CGPointMake(105, 105), 80, CGGradientDrawingOptions.DrawsAfterEndLocation)
    }

將此方法加入到drawRect中

將view加到vc中

 override func viewDidLoad() {
        super.viewDidLoad()
        let ctView = CGView5(frame:CGRectMake(0,64,self.view.frame.width,100))
        ctView.backgroundColor = UIColor.whiteColor()
        self.view.addSubview(ctView)
        
        let ctView1 = CGView6(frame:CGRectMake(0,100,self.view.frame.width,self.view.frame.height-100))
        ctView1.backgroundColor = UIColor.whiteColor()
        self.view.addSubview(ctView1)
    }

運行效果

配圖
漸變填充

有時候 我們需要在一個矩形中填充漸變,在此可以利用漸變裁切來完成

繼續在CGView6中添加以下方法

 /**
     繪制了一個矩形填充的漸變
     
     - parameter context: 上下文對象
     */
    func drawRectWithLinearGradientFill(context:CGContextRef){
        //使用rgb顏色空間
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        //裁切處一塊矩形用于顯示,注意必須先裁切再調用漸變
        //CGContextClipToRect(context, CGRectMake(20, 150, 280, 300))
        //裁切還可以使用UIKit中對應的方法
        UIRectClip(CGRectMake(20, 250, 300, 300))
        
        let compoents:[CGFloat] = [ 248.0/255.0,86.0/255.0,86.0/255.0,1,
            249.0/255.0,127.0/255.0,127.0/255.0,1,
            1.0,1.0,1.0,1.0]
        
        let locations:[CGFloat] = [0,0.4,1]
        let gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, locations.count)
         CGContextDrawLinearGradient(context, gradient, CGPointMake(20, 250), CGPointMake(320, 300), CGGradientDrawingOptions.DrawsAfterEndLocation);
        
    }

結果

配圖
疊加模式

使用Quartz 2D繪圖時后面繪制的圖像會覆蓋前面的,默認情況下如果前面的被覆蓋后將看不到后面的內容,但是有時候這個結果并不是我們想要的,因此在Quartz 2D中提供了填充模式供開發者配置調整。由于填充模式類別特別多。

因此下面以一個例子來說明:

新建一個CGView7

import UIKit

class CGView7: UIView {

    //相信大家對比代碼和顯示效果并不難發現每種疊加的效果。例子中只是使用UIKit的封裝方法進行疊加模式設置,更一般的方法當然是使用CGContextSetBlendMode(CGContextRef context, CGBlendMode mode)方法進行設置。
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        let rect0 = CGRectMake(0, 130.0, 320.0, 50.0)
        let rect1 = CGRectMake(0, 390.0, 320.0, 50.0)
        
        let rect2=CGRectMake(20, 50.0, 10.0, 250.0)
        let rect3=CGRectMake(40.0, 50.0, 10.0, 250.0)
        let rect4=CGRectMake(60.0, 50.0, 10.0, 250.0)
        let rect5=CGRectMake(80.0, 50.0, 10.0, 250.0)
        let rect6=CGRectMake(100.0, 50.0, 10.0, 250.0)
        let rect7=CGRectMake(120.0, 50.0, 10.0, 250.0)
        let rect8=CGRectMake(140.0, 50.0, 10.0, 250.0)
        let rect9=CGRectMake(160.0, 50.0, 10.0, 250.0)
        let rect10=CGRectMake(180.0, 50.0, 10.0, 250.0)
        let rect11=CGRectMake(200.0, 50.0, 10.0, 250.0)
        let rect12=CGRectMake(220.0, 50.0, 10.0, 250.0)
        let rect13=CGRectMake(240.0, 50.0, 10.0, 250.0)
        let rect14=CGRectMake(260.0, 50.0, 10.0, 250.0)
        let rect15=CGRectMake(280.0, 50.0, 10.0, 250.0)
        
        let rect16=CGRectMake(30.0, 310.0, 10.0, 250.0)
        let rect17=CGRectMake(50.0, 310.0, 10.0, 250.0)
        let rect18=CGRectMake(70.0, 310.0, 10.0, 250.0)
        let rect19=CGRectMake(90.0, 310.0, 10.0, 250.0)
        let rect20=CGRectMake(110.0, 310.0, 10.0, 250.0)
        let rect21=CGRectMake(130.0, 310.0, 10.0, 250.0)
        let rect22=CGRectMake(150.0, 310.0, 10.0, 250.0)
        let rect23=CGRectMake(170.0, 310.0, 10.0, 250.0)
        let rect24=CGRectMake(190.0, 310.0, 10.0, 250.0)
        let rect25=CGRectMake(210.0, 310.0, 10.0, 250.0)
        let rect26=CGRectMake(230.0, 310.0, 10.0, 250.0)
        let rect27=CGRectMake(250.0, 310.0, 10.0, 250.0)
        let rect28=CGRectMake(270.0, 310.0, 10.0, 250.0)
        let rect29=CGRectMake(290.0, 310.0, 10.0, 250.0)
        
        UIColor.yellowColor().set()
        UIRectFill(rect0)
        UIColor.greenColor().set()
        UIRectFill(rect1)
        UIColor.redColor().set()
        UIRectFillUsingBlendMode(rect2, CGBlendMode.Clear )
        UIRectFillUsingBlendMode(rect3, CGBlendMode.Color )
        UIRectFillUsingBlendMode(rect4, CGBlendMode.ColorBurn)
        UIRectFillUsingBlendMode(rect5, CGBlendMode.ColorDodge)
        UIRectFillUsingBlendMode(rect6, CGBlendMode.Copy)
        UIRectFillUsingBlendMode(rect7, CGBlendMode.Darken)
        UIRectFillUsingBlendMode(rect8, CGBlendMode.DestinationAtop)
        UIRectFillUsingBlendMode(rect9, CGBlendMode.DestinationIn)
        UIRectFillUsingBlendMode(rect10, CGBlendMode.DestinationOut)
        UIRectFillUsingBlendMode(rect11, CGBlendMode.DestinationOver)
        UIRectFillUsingBlendMode(rect12, CGBlendMode.Difference)
        UIRectFillUsingBlendMode(rect13, CGBlendMode.Exclusion)
        UIRectFillUsingBlendMode(rect14, CGBlendMode.HardLight)
        UIRectFillUsingBlendMode(rect15, CGBlendMode.Hue)
        UIRectFillUsingBlendMode(rect16, CGBlendMode.Lighten)
        
        UIRectFillUsingBlendMode(rect17, CGBlendMode.Luminosity)
        UIRectFillUsingBlendMode(rect18, CGBlendMode.Multiply)
        UIRectFillUsingBlendMode(rect19, CGBlendMode.Normal)
        UIRectFillUsingBlendMode(rect20, CGBlendMode.Overlay)
        UIRectFillUsingBlendMode(rect21, CGBlendMode.PlusDarker)
        UIRectFillUsingBlendMode(rect22, CGBlendMode.PlusLighter)
        UIRectFillUsingBlendMode(rect23, CGBlendMode.Saturation)
        UIRectFillUsingBlendMode(rect24, CGBlendMode.Screen)
        UIRectFillUsingBlendMode(rect25, CGBlendMode.SoftLight)
        UIRectFillUsingBlendMode(rect26, CGBlendMode.SourceAtop)
        UIRectFillUsingBlendMode(rect27, CGBlendMode.SourceIn)
        UIRectFillUsingBlendMode(rect28, CGBlendMode.SourceOut)
        UIRectFillUsingBlendMode(rect29, CGBlendMode.XOR)
        
    }
}

添加到一個新的vc中 效果:

配圖

對比代碼和顯示效果查看每種疊加效果

上下文變換

在view中可以利用transform對試圖進行平移旋轉縮放,繪圖中我們也經常用到圖形形變,在CoreText中繪制文字的時候因為Core Graphics坐標原點在左下角、UIKit在右上角 。所有要通過變換轉過來,下面通過一個圖片的變換演示一下圖形上下文的形變

class CGView8: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        
        let context = UIGraphicsGetCurrentContext()
        self.drawImage(context!)
    }
    /**
     平移旋轉縮放
     
     - parameter context: 上下文對象
     */
    func drawImage(context:CGContextRef){
        //保存初始狀態
        CGContextSaveGState(context);
        
        //形變第一步:圖形上下文向右平移100
        CGContextTranslateCTM(context,100, 0);
        
        //形變第二步:縮放0.8
        CGContextScaleCTM(context, 0.8, 0.8);
        
        //形變第三步:旋轉
        CGContextRotateCTM(context, CGFloat(M_PI_4)/4);
        let img = UIImage(named: "ddla")

        //從某一點開始繪制
        img?.drawAtPoint(CGPoint(x: 0, y: 100))
        
    }
}
配圖
使用Core Graphics繪制圖像

class CGView9: UIView {
    
//    在前面基本繪圖部分,繪制圖像時使用了UIKit中封裝的方法進行了圖像繪制,我們不妨看一下使用Quartz 2D內置方法繪制是什么效果。
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        
        let context = UIGraphicsGetCurrentContext()
        self.drawImage2(context!)
    }
    func drawImage2(context:CGContextRef){
        let image = UIImage(named: "ddla")
        
        let size = UIScreen.mainScreen().bounds.size
        CGContextSaveGState(context)
        let height:CGFloat = 450
        let y:CGFloat = 50
        //圖像繪制
        let rect = CGRectMake(10, y, 300, height)
        
//        在Core Graphics中坐標系的y軸正方向是向上的,坐標原點在屏幕左下角,y軸方向剛好和UIKit中y軸方向相反
        CGContextScaleCTM(context, 1.0, -1.0) //在y軸縮放-1相當于沿著x張旋轉180
        CGContextTranslateCTM(context, 0, -(size.height-(size.height-2*y-height))) //向上平移
        CGContextDrawImage(context, rect, image?.CGImage)
        
    }
    
}

由于坐標問題 我們做了翻轉和平移

配圖
繪制到位圖

利用位圖圖形上下文給一個圖片添加水印,在下面的程序中我們首先創建上下文,然后在上下文中繪制圖片、直線和文本,最后從當前位圖上下文中取得最終形成的新圖片顯示到界面

class SeventhViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let image = self.drawImageAtImageContext()
        let imageView = UIImageView(image: image)
        imageView.center=CGPointMake(160, 284)
        
        self.view.addSubview(imageView)
    }
    
    func drawImageAtImageContext()->UIImage{
        // 獲得一個位圖圖形上下文
        let size = CGSizeMake(300, 200)
        UIGraphicsBeginImageContext(size)
        
        let img = UIImage(named: "ddla")
        img?.drawInRect(CGRectMake(0, 0, 300, 200))//注意繪圖的位置是相對于畫布頂點而言,不是屏幕
        
        //添加水印
        let context = UIGraphicsGetCurrentContext()
        CGContextMoveToPoint(context, 200, 178)
        CGContextAddLineToPoint(context, 265, 178)
        
        UIColor.redColor().setStroke()
        CGContextSetLineWidth(context, 2)
        CGContextDrawPath(context, CGPathDrawingMode.Stroke)
        
        let str = "ZUBER"
        (str as NSString).drawInRect(CGRectMake(200, 158, 100, 30), withAttributes: [NSFontAttributeName:UIFont.boldSystemFontOfSize(18),NSForegroundColorAttributeName:UIColor.redColor()])
        //返回繪制的新圖形
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        
        //保存圖片
//        let data = UIImagePNGRepresentation(newImage)
//        data?.writeToFile("your_path", atomically: true)
        
        //不要忘記關閉對應上下文
        UIGraphicsEndImageContext()
        
        return newImage
    }
    
//    注意:上面這種方式繪制的圖像除了可以顯示在界面上還可以調用對應方法進行保存(代碼注釋中已經包含保存方法);除此之外這種方法相比在drawRect:方法中繪制圖形效率更高,它不用每次展示時都調用所有圖形繪制方法。
    
}

我們直接在vc中繪制了一個image

結果

配圖
繪制pdf

繪制到PDF則要啟用pdf圖形上下文,PDF圖形上下文的創建使用方式跟位圖圖形上下文是類似的,需要注意的一點就是繪制內容到PDF時需要創建分頁,每頁內容的開始都要調用一次IGraphicsBeginPDFPage();方法。下面的示例演示了文本繪制和圖片繪制(其他圖形繪制也是類似的):

class EighthViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.drawContentToPdfContext()
    }
    
    func drawContentToPdfContext(){
    
        //沙盒路徑(也就是我們應用程序文件運行的路徑)
        let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentationDirectory, NSSearchPathDomainMask.UserDomainMask, true)
        
        let path = (paths.first! as NSString).stringByAppendingPathComponent("test.pdf")
        NSFileManager.defaultManager().createFileAtPath(path, contents: nil, attributes: nil)
      
        //啟用pdf圖形上下文
        /**
        path:保存路徑
        bounds:pdf文檔大小,如果設置為CGRectZero則使用默認值:612*792
        pageInfo:頁面設置,為nil則不設置任何信息
        */
        UIGraphicsBeginPDFContextToFile(path, CGRectZero, nil)
//        [kCGPDFContextAuthor:"ZUBER"]
        //由于pdf文檔是分頁的,所以首先要創建一頁畫布供我們繪制
        UIGraphicsBeginPDFPage()
        
        let title = "開心的學習 CoreGraphics 開心的學習Swift"
        let style = NSMutableParagraphStyle()
        style.alignment = NSTextAlignment.Center
        (title as NSString).drawInRect(CGRectMake(26, 20, 300, 50) , withAttributes: [NSFontAttributeName:UIFont.boldSystemFontOfSize(18),NSParagraphStyleAttributeName:style])
        
        let content = "Learn about Apple products, view online manuals, get the latest downloads, and more. Connect with other Apple users, or get service, support, and professional advice from Apple."
        let style2 = NSMutableParagraphStyle()
        style2.alignment = NSTextAlignment.Left
        (content as NSString).drawInRect(CGRectMake(26, 56, 300, 255) , withAttributes: [NSFontAttributeName:UIFont.systemFontOfSize(15),NSParagraphStyleAttributeName:style2,NSForegroundColorAttributeName:UIColor.grayColor()])
        
        let img = UIImage(named: "ddla")
        img?.drawInRect(CGRectMake(316, 20, 290, 305))
        
        let img2 = UIImage(named: "ddla")
        img2?.drawInRect(CGRectMake(6, 320, 600, 281))
        
        //創建新的一頁繼續繪制
        UIGraphicsBeginPDFPage()
        let img3 = UIImage(named: "ddla")
        img3?.drawInRect(CGRectMake(6, 20, 600, 629))
        
        //結束pdf上下文
        UIGraphicsEndPDFContext()
        
        let scrollView = UIScrollView()
        scrollView.frame = CGRectMake(0,64,self.view.frame.width,self.view.frame.height-64)
        scrollView.contentSize = CGSizeMake(self.view.frame.width+200,800)
        //把pdf顯示在view上
        let ctView = CGView10(frame:CGRectMake(0,64,self.view.frame.width+200,800))
        ctView.path = path
        scrollView.addSubview(ctView)
        self.view.addSubview(scrollView)
        ctView.backgroundColor = UIColor.whiteColor()
       
    }
    
}

我們先拿到的一個目錄添加了我們的文件名 然后創建了文件可以繪制 。最后CGView10讀取了一頁

import UIKit

class CGView10: UIView {

    var path:String!
    
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        
        let context = UIGraphicsGetCurrentContext()
        CGContextTranslateCTM(context, 0.0, self.bounds.size.height)
        CGContextScaleCTM(context, 1.0, -1.0)
        let url = NSURL(fileURLWithPath: path) as CFURLRef
        let pdf = CGPDFDocumentCreateWithURL(url)
        let page = CGPDFDocumentGetPage(pdf, 1)
        CGContextSaveGState(context)
        
//        let pdfTransform = CGPDFPageGetDrawingTransform(page, CGPDFBox.CropBox, self.bounds, 0, true)
//        CGContextConcatCTM(context, pdfTransform)

        CGContextDrawPDFPage(context, page);
        CGContextRestoreGState(context);
    }
}

效果

配圖

drawRect的刷新直接調用setNeedsLayout相信大家都知道在適當的時候調用即可。

如果想做畫板應用這個應該會經常使用。

github地址:https://github.com/smalldu/ZZCoreGraphics
參考鏈接:http://www.cnblogs.com/kenshincui/p/3959951.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作為繪圖引擎。它提供了低...
    ShanJiJi閱讀 1,585評論 0 20
  • --繪圖與濾鏡全面解析 概述 在iOS中可以很容易的開發出絢麗的界面效果,一方面得益于成功系統的設計,另一方面得益...
    韓七夏閱讀 2,773評論 2 10
  • 一月添香, 二月夜未央, 三月情詩慌張, 四月對鏡補紅妝, 五月相攜游馬十方, 六月日日笙歌拋流光; 七月為卿斟唱...
    Te小魚兒er閱讀 345評論 0 1
  • 1. 國慶當天晚上十一點多,我還在火車站等車回家,恰那天火車晚點加上換季天氣一直并不算好,外面陰雨蒙蒙,車站里人多...
    文長長閱讀 19,977評論 147 421
  • 文/徐小木 2016-10-04 有時候洋子幾乎不知道,對于家她到底該要怎么樣去定義..... 如果說家是一個...
    徐小木閱讀 378評論 7 4