一、Gradients(漸變)
漸變是從一個顏色到另外一種顏色變化的填充啤誊。Quartz 提供了 CGShadingRef 和 CGGradientRef 兩種數(shù)據(jù)類型來創(chuàng)建軸向或徑向漸變捐寥。
- axial gradient --- 軸向漸變(也稱為線性漸變)沿著由兩個端點連接的軸線漸變帽驯。所有位于垂直于軸線的某條線上的點都具有相同的顏色值寸痢。
- radial gradient --- 徑向漸變是沿著兩個端點連接的軸線放射狀漸變,不過路徑通常由兩個圓來定義哨坪,所有位于以軸線上點為圓心的圓的周長上點都具有相同的顏色值庸疾。
1、CGShading 和 CGGradient 對象的對比
CGGradient 是 CGShading 的子集当编,更易于使用,而 CGShading可以定義更加復(fù)雜的漸變徒溪。這兩個類型都可以繪制軸向漸變和徑向漸變忿偷。
CGGradient | CGShading |
---|---|
可以使用同一個CGGradient對象繪制軸向和徑向漸變 | 需要為軸向漸變和徑向漸變創(chuàng)建不同的 CGShading 對象 |
設(shè)置CGGradient對象的漸變的幾何形狀(軸向或徑向)是在繪制時指定的 | 是在創(chuàng)建時指定的 |
Quartz來計算漸變梯度上每個點對應(yīng)的顏色值(不需要過多的人為干擾) | 必須提供使用 CGFunctionRef 回調(diào)函數(shù)來計算漸變梯度上每個點對應(yīng)的顏色值 |
可以容易的定義多個定位點和顏色 | 需要設(shè)計自己的回調(diào)函數(shù)來定義多個定位點和顏色,所以需要我們手動處理更多的東西 |
2臊泌、CGGradient 的使用
CGGradient 是漸變的抽象定義鲤桥,它只指定了顏色和位置,但沒有指定幾何形狀渠概。我們可以在軸向和徑向幾何形狀中使用它茶凳。這樣使它更容易重用。
使用CGGradient對象創(chuàng)建和畫一個漸變相當(dāng)簡單,需要以下步驟:
- 創(chuàng)建CGGradient對象:
- 使用函數(shù)
CGGradientRef __nullable CGGradientCreateWithColorComponents( CGColorSpaceRef cg_nullable space, const CGFloat * cg_nullable components, const CGFloat * __nullable locations, size_t count)
創(chuàng)建播揪。參數(shù)含義:space --- 顏色空間贮喧,components --- 顏色組件, locations --- 位置組件(如果“ locations”為NULL,第一個顏色在“顏色”將在位置0,最后的顏色在“顏色”將在位置1,其余的將在中間均勻分布猪狈。否則每個位置的值都應(yīng)該是0-1的CGFloat類型箱沦。如果沒有提供0和1的位置,漸變將會使用接近0和1位置的那些值雇庙。)谓形, count --- 想使用的顏色組件數(shù)量。 - 使用函數(shù)
CGGradientRef __nullable CGGradientCreateWithColors( CGColorSpaceRef __nullable space, CFArrayRef cg_nullable colors, const CGFloat * __nullable locations)
創(chuàng)建疆前。這里如果space不為NULL 寒跳,則所有顏色數(shù)組中的顏色都將轉(zhuǎn)換到改顏色空間,若為NULL,則將會使用通用RGB顏色空間竹椒,但是iOS只允許使用設(shè)備空間顏色童太,所以iOS應(yīng)用這里不能為NULL !!!。這里colors是CFArrayRef類型的,這里必須是包含CGColor類型對象的非空數(shù)組康愤。
- 使用函數(shù)
- 繪制漸變:
- 使用函數(shù)
CGContextDrawLinearGradient(CGContextRef cg_nullable c, CGGradientRef cg_nullable gradient, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions options)
繪制線性漸變儡循。 - 使用函數(shù)
CGContextDrawRadialGradient(CGContextRef cg_nullable c, CGGradientRef cg_nullable gradient, CGPoint startCenter, CGFloat startRadius, CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions options)
創(chuàng)建徑向漸變。
- 使用函數(shù)
這里參數(shù)options是一個枚舉類型:
typedef CF_OPTIONS (uint32_t, CGGradientDrawingOptions) {
kCGGradientDrawsBeforeStartLocation = (1 << 0), // 漸變可以波及起點之前
kCGGradientDrawsAfterEndLocation = (1 << 1) // 漸變可以波及終點之后
};
- 當(dāng)不再需要時釋放CGGradient對象征冷。
示例:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGGradientRef gradient;
CGColorSpaceRef colorSpace;
colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 0.1, 0.5, 0.6, 0.8, 1.0};
CGFloat colorComponents[] = {1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
1.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 1.0, 1.0,
0.0, 0.0, 1.0, 1.0};
gradient = CGGradientCreateWithColorComponents(colorSpace, colorComponents, locations, 6);
// CFArrayRef colorArray;
// NSArray * array = @[(id)[UIColor redColor].CGColor, (id)[UIColor cyanColor].CGColor, (id)[UIColor yellowColor].CGColor, (id)[UIColor blueColor].CGColor];
// colorArray = (__bridge CFArrayRef)array;
// gradient = CGGradientCreateWithColors(colorSpace, colorArray, locations);
// CGContextDrawLinearGradient(currentContext, gradient, CGPointMake(50, 50), CGPointMake(150, 150), 0); // 0 代表兩端均不超范圍渲染
CGContextDrawRadialGradient(currentContext, gradient, CGPointMake(50, 50), 30, CGPointMake(150, 150), 100, 0);
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
效果圖:
3择膝、CGShading的使用
CGShadingRef不透明的數(shù)據(jù)類型可以讓你自己控制顏色在每個點的梯度計算。這就必須在創(chuàng)建一個CGShading對象之前,先創(chuàng)建一個CGFunction類型對象(CGFunctionRef)检激,用以定義一個計算顏色漸變的函數(shù)肴捉。CGShading 使用戶有更高的控制權(quán),可以定義更加復(fù)雜的漸變叔收。
?因為創(chuàng)建CGFunctionRef函數(shù)中需要使用回調(diào)函數(shù)齿穗,所以下面從回調(diào)函數(shù)開始一步步去了解CGShading的使用。
- 創(chuàng)建CGFunctionRef函數(shù)中的回調(diào)函數(shù):該回調(diào)函數(shù)是結(jié)構(gòu)體類型:
struct CGFunctionCallbacks {
unsigned int version; // 版本數(shù)據(jù)一般使用0
CGFunctionEvaluateCallback __nullable evaluate; // 用于評估函數(shù)的回調(diào)饺律,一般不可為NULL
CGFunctionReleaseInfoCallback __nullable releaseInfo; // 如果用于評估函數(shù)的回調(diào)中info為NULL,則這里也可以是NULL,
// 若不為NULL,該回調(diào)用于在CGFunction被銷毀時release在CGFunction 創(chuàng)建時傳給function的info參數(shù)信息窃页。
};
typedef struct CGFunctionCallbacks CGFunctionCallbacks;
創(chuàng)建評估函數(shù)回調(diào):樣式必須與下面的類似,函數(shù)名可以隨意复濒,但是參數(shù)必須一致脖卖!
void (*CGFunctionEvaluateCallback)(void * __nullable info, const CGFloat * in, CGFloat * out)
?(1、) info : 這是一個接受CGFunctionCreate函數(shù)傳遞的info信息的參數(shù)巧颈。
?(1畦木、) in:浮點數(shù)的數(shù)組,Quartz 傳遞 in 數(shù)組給回調(diào)砸泛,數(shù)組中的值必須在CGFunction對象定義的輸入值范圍內(nèi)十籍。數(shù)組的大小是由CGFunctionCreate函數(shù)中domainDimension參數(shù)傳遞的數(shù)據(jù)指定的。
?(1唇礁、) out:浮點數(shù)的數(shù)組勾栗,回調(diào)函數(shù)傳遞 out 數(shù)組給 Quartz,它包含用于顏色空間中每個顏色組件的元素及一個 alpha 值垒迂。輸出值應(yīng)該在 CGFunction 對象定義的輸出值范圍內(nèi)械姻。數(shù)組的大小是由CGFunctionCreate函數(shù)中rangeDimension參數(shù)傳遞的數(shù)據(jù)指定的。
? 示例:
// 這一函數(shù)實現(xiàn)的是平方的功能
void evaluateSquare(void *info, const float *inData, float *outData)
{
outData[0] = inData[0] * inData[0];
}
void MyFunctionEvaluateCallback(void * __nullable info, const CGFloat * in, CGFloat * out)
{
CGFloat v;
size_t k, components;
static const CGFloat c[] = {1, 0, .5, 0 }; // 定義基準(zhǔn)顏色數(shù)組
components = (size_t)info;
v = *in; // 獲取輸入的值
for (k = 0; k < components -1; k++){
*out++ = c[k] * v; // 設(shè)置算法机断,獲取想要輸出的結(jié)果楷拳。
// 該結(jié)果是指輸出結(jié)果為在基準(zhǔn)顏色定義上獲取與輸入值相乘結(jié)果后的顏色
// 在下面的例子中輸入的domainDimension參數(shù)為{0, 1}吏奸,所以輸出顏色會為(0欢揖,0,0)和(1奋蔚,0她混,0.5)
}
*out++ = 1; // 將 alpha值恒為 1烈钞。
}
- 創(chuàng)建CGFunctionRef函數(shù):使用函數(shù)
CGFunctionRef __nullable CGFunctionCreate(void * __nullable info, size_t domainDimension, const CGFloat *__nullable domain, size_t rangeDimension, const CGFloat * __nullable range, const CGFunctionCallbacks * cg_nullable callbacks)
。其中參數(shù)的意義:- info : 傳遞給回調(diào)函數(shù)的額外信息的參數(shù)坤按,可以為NULL.
- domainDimension :輸入給回調(diào)函數(shù)的值的數(shù)量毯欣。
- domain:是一個含有2M個元素的字符數(shù)組,M是輸入值的數(shù)量即domainDimension臭脓。對于從0到(M-1)之間的每一個數(shù)字k來說酗钞,必須有domain[2k] <= domain[2k + 1],在該區(qū)間內(nèi)in[k]將會被限制剪切来累,即domain[2k] <= in[k] <= domain[2k + 1]砚作。即:給每個輸入值規(guī)定一范圍,輸入值必須在設(shè)置的范圍以內(nèi)嘹锁。 如果domain 為NULL,則輸入值不會被剪切葫录。但強(qiáng)烈建議設(shè)置domain。
- rangeDimension:從回調(diào)函數(shù)輸出的值的數(shù)量领猾。
- range:與domain一樣米同,不過是針對的輸出值!瘤运!
- callbacks:回調(diào)函數(shù)窍霞。
??示例:
static CGFunctionRef MyGetFunction(CGColorSpaceRef colorspace)
{
size_t numComponents;
static const CGFloat input_value_range [2] = { 0, 1};
static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
static const CGFunctionCallbacks callbacks = { 0, &MyFunctionEvaluateCallback, NULL};
numComponents = 1 + CGColorSpaceGetNumberOfComponents (colorspace);
return CGFunctionCreate ((void *) numComponents, 1, input_value_range, numComponents, output_value_ranges, &callbacks);
}
創(chuàng)建CGShading對象:一、使用函數(shù)
CGShadingRef __nullable CGShadingCreateAxial( CGColorSpaceRef cg_nullable space, CGPoint start, CGPoint end, CGFunctionRef cg_nullable function, bool extendStart, bool extendEnd)
創(chuàng)建線性漸變拯坟。參數(shù):space - 顏色空間、start - 起始點韭山、end - 結(jié)束點郁季、function - 計算函數(shù)、extendStart - 是否渲染起始點以外的區(qū)域钱磅、extendEnd - 是否渲染j結(jié)束點以外的區(qū)域梦裂。 二、使用函數(shù)CGShadingRef __nullable CGShadingCreateRadial( CGColorSpaceRef cg_nullable space, CGPoint start, CGFloat startRadius, CGPoint end, CGFloat endRadius, CGFunctionRef cg_nullable function, bool extendStart, bool extendEnd)
創(chuàng)建徑向漸變盖淡。參數(shù)詳情這里不再贅述年柠。Clip the Context And Drawing CGShading Object(裁剪上下文、繪制CGShading對象)
?繪制漸變的工作原理不同于顏色和圖案可以使用stroke和fill描繪對象褪迟。如果要出現(xiàn)想要的特定形狀需要相應(yīng)的剪切上下文冗恨。使用繪制函數(shù)CGContextDrawShading(CGContextRef cg_nullable c, cg_nullable CGShadingRef shading)
進(jìn)行繪制最后將創(chuàng)建的都要記得釋放掉。
完整示例:線性漸變
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGShadingRef shading;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFunctionRef function = MyGetFunction(colorSpace);
shading = CGShadingCreateAxial(colorSpace, CGPointMake(50, 50), CGPointMake(150, 150), function, NO, NO);
CGContextSaveGState(currentContext);
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath addArcWithCenter:CGPointMake(100, 100) radius:50 startAngle:M_PI endAngle:M_PI * 2 clockwise:YES];
[bezierPath closePath];
CGContextAddPath(currentContext, bezierPath.CGPath);
CGContextClip(currentContext);
CGContextDrawShading(currentContext, shading);
CGColorSpaceRelease(colorSpace);
CGFunctionRelease(function);
CGShadingRelease(shading);
CGContextRestoreGState(currentContext);
}
void MyFunctionEvaluateCallback(void * __nullable info, const CGFloat * in, CGFloat * out)
{
CGFloat v;
size_t k, components;
static const CGFloat c[] = {1, 0, .5, 0 };
components = (size_t)info;
v = *in;
for (k = 0; k < components -1; k++){
*out++ = c[k] * v;
}
*out++ = 1;
}
static CGFunctionRef MyGetFunction(CGColorSpaceRef colorspace)
{
size_t numComponents;
static const CGFloat input_value_range [2] = { 0, 1};
static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
static const CGFunctionCallbacks callbacks = { 0, &MyFunctionEvaluateCallback, NULL};
numComponents = 1 + CGColorSpaceGetNumberOfComponents (colorspace);
return CGFunctionCreate ((void *) numComponents, 1, input_value_range, numComponents, output_value_ranges, &callbacks);
}
完整示例:徑向漸變
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGShadingRef shading;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFunctionRef function = MyGetFunction(colorSpace);
shading = CGShadingCreateRadial(colorSpace, CGPointMake(50, 300), 50, CGPointMake(200, 100), 100, function, NO, NO);
CGContextSaveGState(currentContext);
UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 300, 400)];
CGContextAddPath(currentContext, bezierPath.CGPath);
CGContextClip(currentContext);
CGContextDrawShading(currentContext, shading);
CGColorSpaceRelease(colorSpace);
CGFunctionRelease(function);
CGShadingRelease(shading);
CGContextRestoreGState(currentContext);
}
void MyFunctionEvaluateCallback(void * __nullable info, const CGFloat * in, CGFloat * out)
{
size_t k, components;
double frequency[4] = { 55, 220, 110, 0 };
components = (size_t)info;
for (k = 0; k < components - 1; k++){
*out++ = (1 + sin(*in * frequency[k]))/2;
}
*out++ = 1; // alpha
}
static CGFunctionRef MyGetFunction(CGColorSpaceRef colorspace)
{
size_t numComponents;
static const CGFloat input_value_range [2] = { 0, 1};
static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
static const CGFunctionCallbacks callbacks = { 0, &MyFunctionEvaluateCallback, NULL};
numComponents = 1 + CGColorSpaceGetNumberOfComponents (colorspace);
return CGFunctionCreate ((void *) numComponents, 1, input_value_range, numComponents, output_value_ranges, &callbacks);
}
?從上面兩個示例代碼可以看出這兩個示例代碼最重要的兩個差異的地方就是:1味赃、線性漸變示例代碼創(chuàng)建shading對象使用CGShadingCreateAxial() 函數(shù)掀抹,而徑向漸變示例代碼使用CGShadingCreateRadial() 函數(shù),這決定了繪制漸變的本質(zhì)的不同心俗。2傲武、回調(diào)函數(shù)部分完全不同蓉驹,回調(diào)函數(shù)其實就是決定了計算漸變梯度上每個點對應(yīng)的顏色值。根據(jù)不同的計算方式可以實現(xiàn)各種漸變展示揪利,當(dāng)然這很需要對數(shù)學(xué)函數(shù)的理解~??????态兴。
二、Transparency Layers(透明層)
?透明層由兩個或兩個以上的對象組合而產(chǎn)生一個復(fù)合圖形疟位。復(fù)合結(jié)果被視為一個單獨的對象瞻润。透明層是可以嵌套的。
繪制Transparency Layers:
-
開啟透明層繪制:
- 使用函數(shù)
CGContextBeginTransparencyLayer(CGContextRef cg_nullable c, CFDictionaryRef __nullable auxiliaryInfo)
字典讓你提供選項來指定關(guān)于layer的附加信息,但由于在Quartz 2D API中該字典還沒有被使用,所以傳遞NULLO缀埂8叶! - 使用函數(shù)
CGContextBeginTransparencyLayerWithRect( CGContextRef cg_nullable c, CGRect rect, CFDictionaryRef __nullable auxInfo)
相對于上一函數(shù)多了一個限制邊界的參數(shù)rect罢吃。
- 使用函數(shù)
在透明層中繪制需要組合的對象楚午。
調(diào)用函數(shù)
CGContextEndTransparencyLayer(CGContextRef cg_nullable c)
結(jié)束透明層繪制。
示例:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGSize myShadowOffset = CGSizeMake (-20, 30);
CGContextSetShadow (currentContext, myShadowOffset, 10);
CGContextBeginTransparencyLayer(currentContext, NULL);
CGGradientRef gradientCop;
CGGradientRef gradientBed;
CGColorSpaceRef colorSpace;
colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat colorComponents[] = {1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
1.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 1.0, 1.0,
0.0, 0.0, 1.0, 1.0};
gradientCop = CGGradientCreateWithColorComponents(colorSpace, colorComponents, NULL, 6);
gradientBed = CGGradientCreateWithColorComponents(colorSpace, colorComponents, NULL, 6);
CGContextSaveGState(currentContext);
UIBezierPath * copPath = [UIBezierPath bezierPath];
[copPath moveToPoint:CGPointMake(70, 20)];
[copPath addQuadCurveToPoint:CGPointMake(50, 90) controlPoint:CGPointMake(100, 55)];
[copPath addLineToPoint:CGPointMake(150, 90)];
[copPath addQuadCurveToPoint:CGPointMake(130, 20) controlPoint:CGPointMake(100, 55)];
[copPath closePath];
CGContextAddPath(currentContext, copPath.CGPath);
CGContextClip(currentContext);
CGContextDrawLinearGradient(currentContext, gradientCop, CGPointMake(20, 55), CGPointMake(180, 55), 0);
CGGradientRelease(gradientCop);
CGContextRestoreGState(currentContext);
CGContextSaveGState(currentContext);
UIBezierPath * bedPath = [UIBezierPath bezierPath];
[bedPath moveToPoint:CGPointMake(90, 90)];
[bedPath addLineToPoint:CGPointMake(110, 90)];
[bedPath addLineToPoint:CGPointMake(110, 190)];
[bedPath addQuadCurveToPoint:CGPointMake(100, 210) controlPoint:CGPointMake(115, 195)];
[bedPath addQuadCurveToPoint:CGPointMake(90, 190) controlPoint:CGPointMake(85, 195)];
[bedPath closePath];
CGContextAddPath(currentContext, bedPath.CGPath);
CGContextClip(currentContext);
CGContextDrawRadialGradient(currentContext, gradientBed, CGPointMake(100, 210), 0, CGPointMake(100, 90), 10, 0);
CGGradientRelease(gradientBed);
CGContextRestoreGState(currentContext);
CGColorSpaceRelease(colorSpace);
CGContextEndTransparencyLayer(currentContext);
}