一、引言
CoreGraphics核心圖形框架相較於UIKit框架更加偏於底層。在Objective-C工程中,CoreGraphics其中方法都是采用C語言風格進行編寫的,同時其並不支持Objective-C的自動引用計數,在使用這個框架進行編程時,開發者要手動對內存進行管理。在Swift工程中,Apple使用Swift語言對CoreGraphics礦建進行了重構,將CGPath,CGMutablePaht等都重新定義為了類。CGPath可以理解為圖形的路徑,在Objective-C工程中,其實系統定義的一個內部結構體,開發者不可以直接使用,開發者CGPathRef和CGMutablePathRef別名作為CGPath的引用,實際上,CGPathRef和CGMutablePathRef都是CGPath結構體類型的指針,不同的是一個是const類型不可修改的,一個是可以修改的,系統定義如下:
typedef struct CGPath *CGMutablePathRef;
typedef const struct CGPath *CGPathRef;
二、CGPath創建與內存管理的相關方法
關於CGPath的創建與內存管理的相關方法,列舉如下:
//這個方法獲取CGPath類在CoreGraphics框架中的唯一標識
//CFTypeID 實際上是無符號整型的別名 其為CoreGraphics框架中每個類都定義了一個標識 CGPath為280
CFTypeID CGPathGetTypeID(void);
//這個方法創建一個srtuct CGPath * 指針 可以理解為可變的CGPath類
CGMutablePathRef CGPathCreateMutable(void);
//這個方法通過一個CGPathRef來創建CGPathRef
CGPathRef CGPathCreateCopy(CGPathRef path);
//這個方法在通過CGPathRef創建CGPathRef時會將得路徑進行transform變換后返回
CGPathRef CGPathCreateCopyByTransformingPath(CGPathRef path, const CGAffineTransform * transform);
//這個方法通過CGPathRef創建可變的CGMutablePathRef
CGMutablePathRef CGPathCreateMutableCopy(CGPathRef path);
//意義同上,在創建的CGMutablePathRef基礎上進行一次transform變換在返回
CGMutablePathRef CGPathCreateMutableCopyByTransformingPath(CGPathRef path, const CGAffineTransform * transform)
//這個方法將創建矩形路徑 第一個參數為要繪制的矩形區域 第2個參數為要進行的transform變換
CGPathRef CGPathCreateWithRect(CGRect rect,const CGAffineTransform * transform);
//這個方法將創建橢圓形路徑
CGPathRef CGPathCreateWithEllipseInRect(CGRect rect, const CGAffineTransform * transform);
//這個方法用於創建圓角矩形路徑
/*
rect :繪制的矩形區域
cornerWidth: 橫向圓角尺寸
cornerHeight:縱向圓角尺寸
*/
CGPathRef CGPathCreateWithRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight,const CGAffineTransform * transform);
//這個方法用於創建虛線路徑
/*
這個方法略微有些復雜 其中參數意義如下:
path:要進行虛線化的路徑
phase:從lengths數組的第幾部分開始繪制虛線
lengths:C風格的數組 其中為CGFloat值 表示每段虛線的繪制長度 例如傳入數組為{10,5},則虛線的先繪制長度為10的實線 在繪制長度為5的空白 在進行循環
count:這個參數需要設置為lengths數組的長度
*/
CGPathRef CGPathCreateCopyByDashingPath(CGPathRef path, const CGAffineTransform * transform,CGFloat phase, const CGFloat *lengths, size_t count);
//通過CGPathRef來創建斜線
/*
lineWidth:設置線寬
lineCap:設置線帽風格 可選參數如下:
typedef CF_ENUM(int32_t, CGLineCap) {
kCGLineCapButt, 默認的風格 線的端點精確到點
kCGLineCapRound, 圓滑的端點 線的端點為半徑為線寬一半的圓弧
kCGLineCapSquare 尖銳的過渡
};
lineJoin:設置連接線處的風格 可選參數如下:
typedef CF_ENUM(int32_t, CGLineJoin) {
kCGLineJoinMiter, //以鋒利的角作為連接線的轉折
kCGLineJoinRound, //以圓角作為連接線的轉折
kCGLineJoinBevel //貝塞爾風格的轉折
};
miterLimit:這個值將決定線連接處角的鋒利程度
*/
CGPathRef CGPathCreateCopyByStrokingPath(CGPathRef cg_nullable path, const CGAffineTransform * __nullable transform,CGFloat lineWidth, CGLineCap lineCap,CGLineJoin lineJoin, CGFloat miterLimit);
//手動使CGPathRef引用計數+1
CGPathRef CGPathRetain(CGPathRef cg_nullable path);
//手動使CGPathRef引用計數-1
void CGPathRelease(CGPathRef cg_nullable path);
自定義一個View視圖,在其drawRect方法中進行界面的繪制,示例代碼如下:
- (void)drawRect:(CGRect)rect {
//獲取當前繪圖上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGPoint center = CGPointMake(rect.size.width/2, rect.size.height/2);
//創建圓角矩形路徑
CGPathRef pathRef = CGPathCreateWithRoundedRect(CGRectMake(center.x-50, center.y-50, 100, 100), 30, 10, nil);
//將路徑虛線化
CGFloat floats[] = {10,5};
pathRef = CGPathCreateCopyByDashingPath(pathRef, nil, 0, floats, 2);
//設置繪制顏色
[[UIColor redColor] setStroke];
//將路徑添加到繪圖上下文中
CGContextAddPath(contextRef, pathRef);
//進行繪制
CGContextDrawPath(contextRef, kCGPathStroke);
//內存釋放
CGPathRelease(pathRef);
CGContextRelease(contextRef);
}
運行后效果如下圖所示:
三、CGPath的路徑繪制相關方法
//將路徑移動到一個點作為起點
void CGPathMoveToPoint(CGMutablePathRef path,const CGAffineTransform * m, CGFloat x, CGFloat y);
//將路徑移動到某個點畫出一條線
void CGPathAddLineToPoint(CGMutablePathRef path,const CGAffineTransform * m, CGFloat x, CGFloat y);
//向路徑中添加一段二次貝塞爾曲線
/*
cpx:控制點的x坐標
cpy:控制點的y坐標
*/
void CGPathAddQuadCurveToPoint(CGMutablePathRef path,const CGAffineTransform * m, CGFloat cpx, CGFloat cpy,CGFloat x, CGFloat y);
//添加一段三次貝塞爾曲線
void CGPathAddCurveToPoint(CGMutablePathRef path,const CGAffineTransform * m, CGFloat cp1x, CGFloat cp1y,CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y);
//這個方法用於閉合路徑 調用這個方法后 路徑最后的端點將和起點閉合
void CGPathCloseSubpath(CGMutablePathRef path);
//向路徑中追加一個矩形
void CGPathAddRect(CGMutablePathRef path, const CGAffineTransform * m, CGRect rect);
//向路徑中追加一組矩形
void CGPathAddRects(CGMutablePathRef path, const CGAffineTransform * m, const CGRect * rects,size_t count);
//向路徑中追加一組線條
void CGPathAddLines(CGMutablePathRef path, const CGAffineTransform * m, const CGPoint * __nullable points, size_t count);
//添加一組橢圓
void CGPathAddEllipseInRect(CGMutablePathRef cg_nullable path,const CGAffineTransform * m, CGRect rect);
//向路徑中追加一組圓弧
/*
x:圓心x坐標
y:圓心y坐標
radius:弧線半徑
startAngle:起始角度
endAngle:終止角度
clockwise:是否順時針繪制
*/
void CGPathAddArc(CGMutablePathRef path, const CGAffineTransform * m, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, bool clockwise);
//向路徑中追加一組圓弧
/*
x:圓心x坐標
y:圓心y坐標
radius:弧線半徑
startAngle:起始角度
delta:圓弧繪制的長度 為弧度制 2π為整個圓
*/
void CGPathAddRelativeArc(CGMutablePathRef path, const CGAffineTransform * __nullable matrix, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat delta);
//向路徑中追加一段圓弧 弧線是以(x1,y1)到(x2,y2)為切線的弧線
void CGPathAddArcToPoint(CGMutablePathRef path,const CGAffineTransform * m, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius);
//向路徑中追加一段路徑
void CGPathAddPath(CGMutablePathRef path1,const CGAffineTransform * m, CGPathRef path2);
示例代碼如下:
- (void)drawRect:(CGRect)rect {
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGPoint center = CGPointMake(rect.size.width/2, rect.size.height/2);
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathMoveToPoint(pathRef, nil, center.x, center.y-50);
CGPathAddLineToPoint(pathRef, nil, center.x+100, center.y);
CGPathAddQuadCurveToPoint(pathRef, nil, 0, 0, center.x+100, center.y-100);
CGPathAddRelativeArc(pathRef, nil, 100, 100, 50, 0, M_PI);
CGPathCloseSubpath(pathRef);
[[UIColor redColor] setStroke];
CGContextAddPath(contextRef, pathRef);
CGContextDrawPath(contextRef, kCGPathStroke);
CGPathRelease(pathRef);
CGContextRelease(contextRef);
}
效果如下圖所示:
四、CGPath中的其他方法匯總
//判斷某個路徑是否為空
bool CGPathIsEmpty(CGPathRef path);
//判斷某個路徑是否為某個矩形
bool CGPathIsRect(CGPathRef cg_nullable path, CGRect * rect);
//獲取某個路徑當前繪制所在的點
CGPoint CGPathGetCurrentPoint(CGPathRef path);
//獲取某個路徑包含所有點的尺寸
CGPathGetBoundingBox(CGPathRef cg_nullable path);
//獲取某個路徑的尺寸
CGRect CGPathGetPathBoundingBox(CGPathRef path);
//判斷路徑是否包含某個點
bool CGPathContainsPoint(CGPathRef path, const CGAffineTransform * m, CGPoint point, bool eoFill);
五、關於CGPathElement結構體
當每次向CGPath路徑做操作時,操作的過程實際上都會被記錄下來,每個操作行為節點都被封裝為了CGPathElement結構體,開發者可以通過如下方法來獲取所有操作行為:
CGPathApply(pathRef, nil, func);
CGPathApply()方法中的第3個參數為一個函數指針,示例C函數實現如下:
void func(void * __nullable info,
const CGPathElement * element){
printf("%d",(*element).type);
}
CGPathElement結構體的定義如下:
struct CGPathElement {
//操作節點的類型
CGPathElementType type;
//對應的點集
CGPoint * points;
};
//CGPathElementType枚舉定義如下
typedef CF_ENUM(int32_t, CGPathElementType) {
//移動到點的操作行為
kCGPathElementMoveToPoint,
//添加線的操作行為
kCGPathElementAddLineToPoint,
//添加二次貝塞爾曲線的操作行為
kCGPathElementAddQuadCurveToPoint,
//添加三次貝塞爾曲線的操作行為
kCGPathElementAddCurveToPoint,
//閉合路徑的操作行為
kCGPathElementCloseSubpath
};
原文地址:https://my.oschina.net/u/2340880/blog/757072