一個圖形上下文狀態有很多個圖形上下文設置構成,這個狀態決定了在這個時刻繪畫的行為和外觀。下面列舉了Core Graphics 函數,同時跟著對應的UIKit 提供的封裝好的方便的方法:
線的粗細和虛線樣式
CGContextSetLineWidth, CGContextSetLineDash (and UIBezierPath lineWidth, setLineDash:count:phase:)
線末端樣式和連接樣式
CGContextSetLineCap, CGContextSetLineJoin, CGContextSetMiterLimit (and UIBezierPath lineCapStyle, lineJoinStyle, miterLimit)
線顏色和模式
CGContextSetRGBStrokeColor, CGContextSetGrayStrokeColor, CGContextSet- StrokeColorWithColor, CGContextSetStrokePattern (and UIColor setStroke)
填充顏色和模式
CGContextSetRGBFillColor, CGContextSetGrayFillColor, CGContextSetFill- ColorWithColor, CGContextSetFillPattern (and UIColor setFill)
陰影
CGContextSetShadow, CGContextSetShadowWithColor
整體透明度和組合
CGContextSetAlpha, CGContextSetBlendMode
反鋸齒
CGContextSetShouldAntialias
其它額外的設置
裁剪區域
在裁剪區域外進行繪制,其實不會真的被繪制
轉換(或者“CTM”,當前轉換矩陣)
可以改變你指定的點在隨后的繪制命令中,如何對應到畫布的實際空間上。
下面是一些可能會用到的路徑繪制命令:
當前點的位置
CGContextMoveToPoint
描摹一條線
CGContextAddLineToPoint, CGContextAddLines
描摹一個矩形
CGContextAddRect, CGContextAddRects
描摹一個橢圓或者圓形
CGContextAddEllipseInRect
描摹一個圓弧
CGContextAddArcToPoint, CGContextAddArc
描摹有一個或者兩個控制點的貝塞爾曲線
CGContextAddQuadCurveToPoint, CGContextAddCurveToPoint
關閉當前路徑
CGContextClosePath 這個會在路徑的最后一個點和第一個點之間添加一條線,如果你是打算要填充這個路徑,那么不需要調用這個方法,填充操作會自動幫我們完成。
描邊或者填充當前路徑
CGContextStrokePath, CGContextFillPath, CGContextEOFillPath, CGContext- DrawPath. 描邊或者填充操作都會清除這個路徑。如果你想既描邊又填充路徑,可以使用CGContextDrawPath。 如果你僅僅先通過 CGContextStrokePath 描邊,你就不可能再填充這個路徑了,因為這個路徑已經被清除了。
當然還有很多其它更方便的函數來創建一個路徑,然后描邊或者填充:CGContextStrokeLineSegments, CGContextStrokeRect, CGContextStrokeRectWithWidth, CGContextFillRect, CGContextFillRects, CGContextStrokeEllipseInRect, CGContextFillEllipseInRect.
一個路徑可以是混合而成的,意味著這個路徑由多個獨立的塊組成。例如,一個路徑可以包括兩個分開的閉合的形狀:一個矩形和一個圓形。 當你在構造一個路徑的某個時刻(也就是在描摹出一個路徑之后,同時沒有描邊,填充來清除路徑,或者調用 CGContextBeginPath)調用 CGContextMoveToPoint,你提起畫筆,然后移動到一個新的位置,接著準備開始一個獨立的一塊同樣的路徑。如果你擔心之前繪制的路徑會被清除,可以調用CGContextBeginPath 來指定你開始一段新的不同的路徑,在蘋果的很多例子中都會這么做,但是我在實際中通常發現不需要。
為了說明上面路徑繪制的命令,我會生成一個向上的箭頭,如下圖所示。這可能不是創建一個箭頭的最好方式,同時我也會故意不使用一個方便的函數,但是過程是清晰的:
// obtain the current graphics contextCGContextRef con = UIGraphicsGetCurrentContext();// draw a black (by default) vertical line, the shaft of the arrowCGContextMoveToPoint(con, 100, 100);CGContextAddLineToPoint(con, 100, 19);CGContextSetLineWidth(con, 20);CGContextStrokePath(con);// draw a red triangle, the point of the arrowCGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);CGContextMoveToPoint(con, 80, 25);CGContextAddLineToPoint(con, 100, 0);CGContextAddLineToPoint(con, 120, 25);CGContextFillPath(con);// snip a triangle out of the shaft by drawing in Clear blend modeCGContextMoveToPoint(con, 90, 101);CGContextAddLineToPoint(con, 100, 90);CGContextAddLineToPoint(con, 110, 101);CGContextSetBlendMode(con, kCGBlendModeClear);CGContextFillPath(con);
如果你想復用或者分享一個路徑,可以把這個路徑包裝成 CGPath,實際上就是CGPathRef。你也可以創建一個新的CGMutablePathRef ,像CGContext 中的路徑構造方法一樣,使用不同的CGPath 方法來構造路徑,或者你可以使用CGContextCopyPath 來復制圖形上下文中當前的路徑。還有一些CGPath方法用來基于簡單的幾何圖形來創建一個路徑(CGPathCreateWithRect, CGPathCreateWithEllipseInRect)或者基于一個已存在的路徑(CGPathCreateCopyByStrokingPath, CGPathCreateCopyByDashing- Path, CGPathCreateCopyByTransformingPath)。
UIKit 中的UIBezierPath封裝了CGPath,同樣也提供了一些路徑構造方法,例如 moveToPoint:, addLineToPoint:, bezierPathWithRect:, bezierPathWithOvalInRect:, addArcWithCenter:radius:startAngle:endAngle:clockwise:, addQuadCurveToPoint:controlPoint:, addCurveToPoint:controlPoint1:controlPoint2:, closePath。同樣,UIBezierPath也提供了一個非常有用的方法:bezierPathWithRoundedRect:cornerRadius: —— 僅僅使用Core Graphics 方法來繪制圓角矩形是非常繁瑣的。當你調用UIBezierPath 實例方法 fill 或者 stroke (或者 fillWithBlendMode:alpha: 或者 strokeWithBlend- Mode:alpha:)時,當前圖形上下文狀態會被保存,被封裝好的CGPath 路徑會變成當前上下文的繪制路徑,然后描邊或者填充該路徑,最后當前上下文會被恢復原來狀態。
下面我們使用UIKit 提供的UIBezierPath 來重寫上面的箭頭:
UIBezierPath* p = [UIBezierPath bezierPath];[p moveToPoint:CGPointMake(100,100)];[p addLineToPoint:CGPointMake(100, 19)];[p setLineWidth:20];[p stroke];[[UIColor redColor] set];[p removeAllPoints];[p moveToPoint:CGPointMake(80,25)];[p addLineToPoint:CGPointMake(100, 0)];[p addLineToPoint:CGPointMake(120, 25)];[p fill];[p removeAllPoints];[p moveToPoint:CGPointMake(90,101)];[p addLineToPoint:CGPointMake(100, 90)];[p addLineToPoint:CGPointMake(110, 101)];[p fillWithBlendMode:kCGBlendModeClear alpha:1.0];
路徑的另一種使用方式是遮罩某一塊區域,防止在未來的繪制中被改變。這種就是裁剪。默認,一個圖形上下文的裁剪區域就是整一個圖形上下文,你可以在上下文中任意繪制。
裁剪區域是整個上下文的一個功能,任何新的裁剪區域會被應用到現有的裁剪區域,相交而成。如果你應用了自己的裁剪區域,在后面想移除這個裁剪區域的辦法就只能把你的處理包含在 CGContextSaveGState和CGContextRestoreGState方法之間。
為了說明這一點,我將使用裁剪重寫生成我們原來的箭頭符號,而不是使用混合模式在箭頭尾部“切出”三角缺口。這樣會有些棘手,因為我們想要的不是裁剪三角形內部的區域,而是外面的區域。為了解析這個,我們使用包含超過一個閉合區域的混合路徑 —— 一個三角形,以及整個繪制區域(我們可以使用CGContextGetClipBoundingBox 獲?。?/p>
當填充一個混合路徑時,或者使用路徑來表示一個裁剪區域時,系統遵循兩個準則:
非零環繞數規則(Winding rule)
環繞(winding)就是一個路徑環繞的方向,分順時針(正方向)和逆時針(負方向)。在圖形學中判斷一個點是否在多邊形內,若多邊形不是自相交的,那么可以簡單的判斷這個點在多邊形內部還是外部;若多邊形是自相交的,那么就需要根據非零環繞數規則和奇-偶規則判斷。這里推薦一篇文章:http://blog.csdn.net/freshforiphone/article/details/8273023。
奇-偶規則(Odd-even Rule)
從任意位置p作一條射線,若與該射線相交的多邊形邊的數目為奇數,則p是多邊形內部點,否則是外部點。
這里我們使用簡單的奇-偶規則,所以我們使用CGContextEOClip 來設定裁剪區域,然后繪制箭頭:
// obtain the current graphics contextCGContextRef con = UIGraphicsGetCurrentContext();// punch triangular hole in context clipping regionCGContextMoveToPoint(con, 90, 100);CGContextAddLineToPoint(con, 100, 90);CGContextAddLineToPoint(con, 110, 100);CGContextClosePath(con);CGContextAddRect(con, CGContextGetClipBoundingBox(con));CGContextEOClip(con);// draw the vertical lineCGContextMoveToPoint(con, 100, 100);CGContextAddLineToPoint(con, 100, 19);CGContextSetLineWidth(con, 20);CGContextStrokePath(con);// draw the red triangle, the point of the arrowCGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);CGContextMoveToPoint(con, 80, 25);CGContextAddLineToPoint(con, 100, 0);CGContextAddLineToPoint(con, 120, 25);CGContextFillPath(con);
UIBezierPath 的裁剪命令是 usesEvenOddFillRule 和 addClip。
漸變可以很簡單,也可以很負責,這里只講解簡單的。一個簡單的漸變是由一個起始點的顏色和一個結束點的顏色,再加上(可選的)一個中間點的顏色值決定,然后就會在上下文中在兩個指定的點以線性或者反射形式繪制。
你不能使用漸變來作為路徑的填充顏色,但是你可以使用裁剪來規定一個路徑形狀的漸變。為了說明這一點,我會重畫我們的箭頭,使用一個線性漸變:
// obtain the current graphics contextCGContextRef con = UIGraphicsGetCurrentContext();CGContextSaveGState(con);// punch triangular hole in context clipping regionCGContextMoveToPoint(con, 90, 100);CGContextAddLineToPoint(con, 100, 90);CGContextAddLineToPoint(con, 110, 100);CGContextClosePath(con);CGContextAddRect(con, CGContextGetClipBoundingBox(con));CGContextEOClip(con);// draw the vertical line, add its shape to the clipping regionCGContextMoveToPoint(con, 100, 100);CGContextAddLineToPoint(con, 100, 19);CGContextSetLineWidth(con, 20);CGContextReplacePathWithStrokedPath(con);CGContextClip(con);// draw the gradientCGFloat locs[3] = { 0.0, 0.5, 1.0 };CGFloat colors[12] = { 0.3,0.3,0.3,0.8, // starting color, transparent gray 0.0,0.0,0.0,1.0, // intermediate color, black 0.3,0.3,0.3,0.8 // ending color, transparent gray};CGColorSpaceRef sp = CGColorSpaceCreateDeviceGray();CGGradientRef grad = CGGradientCreateWithColorComponents (sp, colors, locs, 3);CGContextDrawLinearGradient ( con, grad, CGPointMake(89,0), CGPointMake(111,0), 0);CGColorSpaceRelease(sp);CGGradientRelease(grad);CGContextRestoreGState(con); // done clipping// draw the red triangle, the point of the arrowCGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);CGContextMoveToPoint(con, 80, 25);CGContextAddLineToPoint(con, 100, 0);CGContextAddLineToPoint(con, 120, 25);CGContextFillPath(con);
調用CGContextReplacePathWithStrokedPath 這個方法來防止描邊當前的路徑。
新聞熱點
疑難解答