最近在看《iOS 6 Programming Cookbook》的翻譯版粱挡,它由DevDiv論壇的網(wǎng)友翻譯何暮,原文地址:點(diǎn)擊跳轉(zhuǎn)裤园。由于下載的pdf都有水印,并且排版不是很好泻肯,特別是代碼排版渊迁,基本不能看,所以這里就整理了一下灶挟,方便再次查看琉朽。另外把里面提到的點(diǎn)寫了一個demo,由于里面一些代碼現(xiàn)在已經(jīng)廢棄稚铣,所以demo中都是用的新api箱叁,下載地址在這里:圖形與動畫Demo。
1.8 為形狀增加陰影
使用 CGContextSetShadow
惕医。用core graphic
s繪制陰影很容易蝌蹂。圖形環(huán)境是容納陰影的元素。就是說,你要先對環(huán)境應(yīng)用陰影,再繪制需要陰影的形狀,最后要將陰影從環(huán)境上移除(或者設(shè)置一個新環(huán)境)曹锨。
在 core graphics
中,我們可以使用下列兩個過程來為圖形環(huán)境應(yīng)用陰影:
CGContextSetShadow 過程
這個過程會創(chuàng)建黑色或灰色的陰影,它接受三個參數(shù):
- 要使用陰影的圖形環(huán)境;
- 陰影的位移,由 CGSize 類型值指定,從每個形狀要應(yīng)用陰影的右下部分開始剃允。位移 的 x 值越大,形狀右邊的陰影就擴(kuò)散得越遠(yuǎn)沛简。位移的 y 值越大,下部的陰影就越低;
- 陰影的模糊值,以浮點(diǎn)值(CGFloat)來指定斥废。指定 0.0f 將導(dǎo)致陰影成為固態(tài)形狀椒楣。這個 值越高,陰影就越模糊。
CGContextSetShadowWithColor
這個方法接受的參數(shù)和 CGContextSetShadow
完全相同,不過加了一個 CGColorRef 類型的參數(shù),用于設(shè)置陰影的顏色牡肉。
在此前我 到,圖形環(huán)境會保留陰影屬性,直到我們顯式的移除陰影捧灰。我們看一個例子來讓這一點(diǎn)更清楚吧。下面我們編寫代碼來繪制兩個矩形,第一個有陰影,第二個沒有统锤。我們這樣繪制第一個矩形:
- (void) drawRectAtTopOfScreen
{
/* Get the handle to the current context */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetShadowWithColor(currentContext, CGSizeMake(10.0f, 10.0f), 20.0f, [[UIColor grayColor] CGColor]);
/* Create the path first. Just the path handle. */
CGMutablePathRef path = CGPathCreateMutable();
/* Here are the rectangle boundaries */
CGRect firstRect = CGRectMake(55.0f, 60.0f, 150.0f, 150.0f);
/* Add the rectangle to the path */
CGPathAddRect(path,NULL,firstRect);
/* Add the path to the context */
CGContextAddPath(currentContext, path);
/* Set the fill color to cornflower blue */
[[UIColor colorWithRed:0.20f green:0.60f blue:0.80f alpha:1.0f] setFill];
/* Fill the path on the context */
CGContextDrawPath(currentContext, kCGPathFill);
/* Dispose of the path */
CGPathRelease(path);
}
在視圖對象的 drawRect:方法里調(diào)用上面的方法,我們會看到屏幕上畫出了一 個帶有漂亮陰影的矩形,如下圖:
下面我們畫第二個矩形,我們沒有要求有陰影,但是我們保持了在繪制第一個矩形時圖
形環(huán)境的陰影屬性:
- (void) drawRectAtBottomOfScreen
{
/* Get the handle to the current context */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGMutablePathRef secondPath = CGPathCreateMutable();
CGRect secondRect = CGRectMake(150.0f, 250.0f, 100.0f,100.0f);
CGPathAddRect(secondPath,NULL,secondRect);
CGContextAddPath(currentContext, secondPath);
[[UIColor purpleColor] setFill];
CGContextDrawPath(currentContext, kCGPathFill);
CGPathRelease(secondPath);
}
drawRect:
方法首先調(diào)用了 drawRectAtTopOfScreen
方法,此后,調(diào)用 drawRectAtBottomOfScreen
方法毛俏。我們在 drawRectAtBottomOfScreen
中沒有要求一個陰影, 但是如果你跑一下程序,你會看到類似下圖 所示的結(jié)果
你馬上觀察到屏幕下方的第二個矩形被應(yīng)用了陰影效果。為了避免這個,我們將在對圖
形環(huán)境應(yīng)用陰影效果之前保存它的狀態(tài),并在我們想去掉陰影效果時,恢復(fù)之前的狀態(tài)饲窿。
廣而言之,圖形環(huán)境狀態(tài)的存取不僅限于陰影煌寇。恢復(fù)圖形環(huán)境的狀態(tài)會恢復(fù)一切(填充
色,字體,線寬,等等)到你之前的設(shè)置逾雄。所以如果你同時也設(shè)置了填充和畫筆顏色,這些
顏色也會被重置阀溶。
你可以通過 CGContextSaveGState
過程來保存圖形環(huán)境的狀態(tài),而通過 CGContextRestoreGState
過程恢復(fù)之前的狀態(tài)腻脏。所以如果修改 drawRectAtTopOfScreen 過 程,在應(yīng)用陰影前保存圖形環(huán)境狀態(tài),并在繪制路徑后恢復(fù)其狀態(tài),我們將得到不同的結(jié) 果,如下圖 所示:
修改后的代碼:
- (void) drawRectAtTopOfScreen
{
/* Get the handle to the current context */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
//保存當(dāng)前圖形上下文狀態(tài)
CGContextSaveGState(currentContext);
CGContextSetShadowWithColor(currentContext, CGSizeMake(10.0f, 10.0f), 20.0f, [[UIColor grayColor] CGColor]);
/* Create the path first. Just the path handle. */
CGMutablePathRef path = CGPathCreateMutable();
/* Here are the rectangle boundaries */
CGRect firstRect = CGRectMake(55.0f, 100.0f, 150.0f, 150.0f);
/* Add the rectangle to the path */
CGPathAddRect(path,NULL,firstRect);
/* Add the path to the context */
CGContextAddPath(currentContext, path);
/* Set the fill color to cornflower blue */
[[UIColor colorWithRed:0.20f green:0.60f blue:0.80f alpha:1.0f] setFill];
/* Fill the path on the context */
CGContextDrawPath(currentContext, kCGPathFill);
/* Dispose of the path */
CGPathRelease(path);
//恢復(fù)當(dāng)前圖形上下文狀態(tài)
CGContextRestoreGState(currentContext);
}
1.9 繪制漸變
使用 CGGradientCreateWithColor
函數(shù)。
core graphics
允許程序員創(chuàng)建兩類漸變:軸向的(axial)
和放射狀的(radial)
银锻。
這里只討論軸向漸變永品。軸向漸變是以一種顏色為起點(diǎn),而以另一種顏色為終點(diǎn)的漸變(雖然可以在起點(diǎn)和終點(diǎn)用一種顏色,但是那不會產(chǎn)生漸變)』魑常“軸向”的意思是和某個軸有關(guān)鼎姐。 兩個點(diǎn)(起點(diǎn)和終點(diǎn))形成一條線段,這就是漸變要被繪制的軸。
為了創(chuàng)建一個軸向漸變,你必須調(diào)用 CGGradientCreateWithColorComponents
函數(shù)掉弛。函數(shù)的返回值是一個 CGGradientRef
類型的漸變症见。它是漸變的句柄。當(dāng)你用完漸變后,你必須調(diào)用 CGGradientRelease
釋放掉殃饿。
CGGradientCreateWithColorComponents
函數(shù)接受四個參數(shù):
- 顏色空間
這是一個顏色范圍的容器,這個參數(shù)必須是CGColorSpaceRef
類型的,我們只要傳入CGColorSpaceCreateDeviceRGB
函數(shù)的返回值即可,該函數(shù)會給我們一個 RGB 顏色空間谋作。
- 顏色組件的數(shù)組(詳情參見 1.2 小節(jié))
這個數(shù)組必須包含紅,綠,藍(lán)和 alpha 值,都以 CGFloat 表示。數(shù)組中元素的個數(shù)和下面兩個參數(shù)緊密相連乎芳。你必須在這個數(shù)組中包含足夠的值來指定第四個參數(shù)中的位置數(shù)量遵蚜。 所以,如果你請求兩個位置(起點(diǎn)和終點(diǎn)),你就必須在數(shù)組中提供兩種顏色。因為每種顏色都是由紅奈惑、綠吭净、藍(lán)和 alpha 組成,這個數(shù)組必須有 2X4 項:兩種顏色分別 4 個。
顏色數(shù)組中的顏色位置
這個參數(shù)控制了漸變過度的迅速程度肴甸。元素的個數(shù)一定要和第 4 個參數(shù)一樣寂殉。如果我請求 4 個顏色,比如說,我們希望第 1 種顏色為起始色,最后一種顏色為終止色,這時我們就要提供包含兩個 CGFloat 類型元素的數(shù)組,第一個元素設(shè)置為 0.0f(顏色數(shù)組的第一個),第二個元素設(shè)置為 3.0f(顏色數(shù)組的第 4 個)。中間的兩個顏色的值決定了在起點(diǎn)和終點(diǎn)間如何插入顏色原在。位置數(shù)量
指定我們想要有多少種顏色和位置友扰。
我們來看例子。假設(shè)我們想繪制如圖 圖9-1 所示的漸變,下面是步驟:
- 選擇漸變的開始和結(jié)束點(diǎn) -- 它變換的軸庶柿。這里,我們選擇了從左向右移動村怪。想像你在沿著一條假想的水平線移動時改變顏色。沿著那條線,我們會傳播這些顏色使得每條垂直于這條水平線的線上只包含一種顏色浮庐。這里,這些垂直的線將會是圖 9-1 中的垂線甚负。近距離觀察那些垂直線,其中的每一條從上到下都只包含一種顏色。這就是軸向漸變的原理审残。
- 如前所述,現(xiàn)在我們要創(chuàng)建一個顏色空間來作為
CGGradientCreateWithColorComponents
函數(shù)的第一個參數(shù):
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
在我們用完這個顏色空間后,我們會釋放它梭域。
- 根據(jù)圖 9-1 所選擇的顏色,我們選擇藍(lán)色作為起點(diǎn)(左邊),綠色作為終點(diǎn)(右邊)。我選擇的變量名(
startColorComponents
和endColorComponents
)是特意選定來幫助我們記住我們對每個顏色在做的事情搅轿。實際上我們會使用數(shù)組位置來指定起點(diǎn)和終點(diǎn):
UIColor *startColor = [UIColor blueColor];
CGFloat *startColorComponents =
(CGFloat *)CGColorGetComponents([startColor CGColor]); UIColor *endColor = [UIColor greenColor];
CGFloat *endColorComponents =
(CGFloat *)CGColorGetComponents([endColor CGColor]);
- 在你獲得每個顏色的組件后碰辅,我們將他們放到一個簡單的數(shù)組中,以傳入
CGGradientCreateWithColorComponents
函數(shù):
CGFloat colorComponents[8] = {
// Four components of the blue color (RGBA)
startColorComponents[0],
startColorComponents[1],
startColorComponents[2],
startColorComponents[3], /* First color = blue */
//Four components of the green color (RGBA)
endColorComponents[0],
endColorComponents[1],
endColorComponents[2],
endColorComponents[3], /* Second color = green */
};
- 因為這個數(shù)組只有兩種顏色,我們需要指定第一個是漸變的最開始(位置 0.0)而第二個在最后(位置 1.0)介时。讓我們將這些索引放到數(shù)組中作為參數(shù)傳遞到
CGGradientCreateWithColorComponents
函數(shù):
CGFloat colorIndices[2] = {
0.0f, /* Color 0 in the colorComponents array */
1.0f, /* Color 1 in the colorComponents array */
};
- 現(xiàn)在我們要做的只剩下用之前創(chuàng)建的參數(shù)實際調(diào)用
CGGradientCreateWithColorComponents
函數(shù):
CGGradientRef gradient = CGGradientCreateWithColorComponents
(colorSpace,
(const CGFloat *)&colorComponents,
(const CGFloat *)&colorIndices,
2);
- 非常棒!現(xiàn)在我們在
gradient
變量中保存了我們的漸變對象没宾。在我們忘記之前,還必須釋放之前在CGColorSpaceCreateDeviceRGB
函數(shù)中創(chuàng)建的顏色空間:
CGColorSpaceRelease(colorSpace);
現(xiàn)在我們將使用 CGContextDrawLinearGradient 來往圖形環(huán)境上面繪制軸向漸變凌彬。這個 過程接受 5 個參數(shù):
- 圖形環(huán)境
指定軸向漸變被繪制到的圖形環(huán)境 - 軸向漸變
軸向漸變對象的句柄。我們在CGGradientCreateWithColorComponents
函數(shù)中創(chuàng)建這個 漸變對象循衰。 - 起點(diǎn)
圖形環(huán)境中的一點(diǎn),由 CGPoint 指定,它指定了漸變的起點(diǎn)铲敛。 - 終點(diǎn)
圖形環(huán)境中的一點(diǎn),由 CGPoint 指定,它指定了漸變的終點(diǎn)。 - 漸變繪制選項
指定了當(dāng)你指定的起點(diǎn)或終點(diǎn)不是圖形環(huán)境的邊緣時的行為。你可以使用你的開始色或
結(jié)束色來填充漸變之外的區(qū)域。指定下列參數(shù)值之一:
kCGGradientDrawsAfterEndLocation
在漸變終點(diǎn)之后將漸變擴(kuò)展到所有點(diǎn)
kCGGradientDrawsBeforeStartLocation
在漸變起點(diǎn)之前將漸變擴(kuò)展到所有點(diǎn)
0
不會以任何方式擴(kuò)展?jié)u變
為了在兩端都擴(kuò)展顏色,可以用邏輯 OR(使用|操作符)同時指定“之后”和“之前”參 數(shù)巢墅。我們看下面的例子:
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGPoint startPoint, endPoint;
startPoint = CGPointMake(0.0f, screenBounds.size.height / 2.0f);
endPoint = CGPointMake(screenBounds.size.width, startPoint.y);
CGContextDrawLinearGradient (currentContext, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
代碼結(jié)束位置所釋放的漸變句柄是我們在之前的示例代碼中創(chuàng)建的。
這段代碼的輸出顯然如圖 9-1 所示先鱼。因為我們從最左邊的點(diǎn)開始將漸變拖拽到最右邊的點(diǎn),所以我們不能利用 CGContextDrawLinearGradient
過程最后的“漸變繪制選項”參數(shù)。 讓我們修改一下吧?繪制一個如圖 9-2 所示的漸變?nèi)绾?
我們會使用這部分早先用過的相同的代碼來得到結(jié)果:
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
UIColor *startColor = [UIColor orangeColor];
CGFloat *startColorComponents = (CGFloat *)CGColorGetComponents([startColor CGColor]);
UIColor *endColor = [UIColor blueColor];
CGFloat *endColorComponents = (CGFloat *)CGColorGetComponents([endColor CGColor]);
CGFloat colorComponents[8] = {
/* Four components of the orange color (RGBA) */ startColorComponents[0],
startColorComponents[1],
startColorComponents[2],
startColorComponents[3], /* First color = orange */
/* Four components of the blue color (RGBA) */
endColorComponents[0],
endColorComponents[1],
endColorComponents[2],
endColorComponents[3], /* Second color = blue */
};
CGFloat colorIndices[2] = {
0.0f, /* Color 0 in the colorComponents array */
1.0f, /* Color 1 in the colorComponents array */
};
CGGradientRef gradient = CGGradientCreateWithColorComponents (colorSpace,
(const CGFloat *)&colorComponents,
(const CGFloat *)&colorIndices,2);
CGColorSpaceRelease(colorSpace);
CGPoint startPoint, endPoint;
startPoint = CGPointMake(120, 260);
endPoint = CGPointMake(200.0f, 220);
CGContextDrawLinearGradient (currentContext, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
CGContextRestoreGState(currentContext);
}
你可能難以理解將 kCGGradientDrawsBeforeStartLocation
和 kCGGradientDrawsAfterEndLocation
值混合傳入 CGContextDrawLinearGradient
過程,是如何 產(chǎn)生如圖 9-2 所示的對角線效果的奸鬓。那么讓我們將這些值從 CGContextDrawLinearGradient
過程的那個參數(shù)中去掉,僅僅像之前一樣傳入 0焙畔。圖9-3 顯示了結(jié)果:
很容易得出結(jié)論,圖 9-2 和圖 9-3 中使用的是相同的漸變。但是,圖 9-2 中的漸 變擴(kuò)展了起點(diǎn)和終點(diǎn)的顏色,使?jié)u變充滿整個圖形環(huán)境,這就是為什么你能看見整個屏幕被顏色所覆蓋串远。