圖形與動畫(三)--附Demo

最近在看《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 graphics繪制陰影很容易蝌蹂。圖形環(huán)境是容納陰影的元素。就是說,你要先對環(huán)境應(yīng)用陰影,再繪制需要陰影的形狀,最后要將陰影從環(huán)境上移除(或者設(shè)置一個新環(huán)境)曹锨。

core graphics 中,我們可以使用下列兩個過程來為圖形環(huán)境應(yīng)用陰影:

CGContextSetShadow 過程 

這個過程會創(chuàng)建黑色或灰色的陰影,它接受三個參數(shù):

  1. 要使用陰影的圖形環(huán)境;
  2. 陰影的位移,由 CGSize 類型值指定,從每個形狀要應(yīng)用陰影的右下部分開始剃允。位移 的 x 值越大,形狀右邊的陰影就擴(kuò)散得越遠(yuǎn)沛简。位移的 y 值越大,下部的陰影就越低;
  3. 陰影的模糊值,以浮點(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)用上面的方法,我們會看到屏幕上畫出了一 個帶有漂亮陰影的矩形,如下圖:

圖8-1 對矩形使用陰影

下面我們畫第二個矩形,我們沒有要求有陰影,但是我們保持了在繪制第一個矩形時圖
形環(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é)果

圖8-2 應(yīng)用到第二個矩形的陰影不是我們想要的.png

你馬上觀察到屏幕下方的第二個矩形被應(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é) 果,如下圖 所示:

圖8-3 保存圖形環(huán)境狀態(tài)來準(zhǔn)確的使用陰影

修改后的代碼:

- (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)银锻。

圖9-1 一個開始于藍(lán)色結(jié)束于綠色的軸向漸變

這里只討論軸向漸變永品。軸向漸變是以一種顏色為起點(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 所示的漸變,下面是步驟:

  1. 選擇漸變的開始和結(jié)束點(diǎn) -- 它變換的軸庶柿。這里,我們選擇了從左向右移動村怪。想像你在沿著一條假想的水平線移動時改變顏色。沿著那條線,我們會傳播這些顏色使得每條垂直于這條水平線的線上只包含一種顏色浮庐。這里,這些垂直的線將會是圖 9-1 中的垂線甚负。近距離觀察那些垂直線,其中的每一條從上到下都只包含一種顏色。這就是軸向漸變的原理审残。
  2. 如前所述,現(xiàn)在我們要創(chuàng)建一個顏色空間來作為 CGGradientCreateWithColorComponents 函數(shù)的第一個參數(shù):
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

在我們用完這個顏色空間后,我們會釋放它梭域。

  1. 根據(jù)圖 9-1 所選擇的顏色,我們選擇藍(lán)色作為起點(diǎn)(左邊),綠色作為終點(diǎn)(右邊)。我選擇的變量名(startColorComponentsendColorComponents)是特意選定來幫助我們記住我們對每個顏色在做的事情搅轿。實際上我們會使用數(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]);
  1. 在你獲得每個顏色的組件后碰辅,我們將他們放到一個簡單的數(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 */
 };
  1. 因為這個數(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 */
};
  1. 現(xiàn)在我們要做的只剩下用之前創(chuàng)建的參數(shù)實際調(diào)用 CGGradientCreateWithColorComponents 函數(shù):
CGGradientRef gradient = CGGradientCreateWithColorComponents 
(colorSpace,
(const CGFloat *)&colorComponents,
(const CGFloat *)&colorIndices,
2);
  1. 非常棒!現(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)绾?

圖 9-2 一個對起點(diǎn)和終點(diǎn)進(jìn)行顏色擴(kuò)展的漸變

我們會使用這部分早先用過的相同的代碼來得到結(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);
}

你可能難以理解將 kCGGradientDrawsBeforeStartLocationkCGGradientDrawsAfterEndLocation 值混合傳入 CGContextDrawLinearGradient 過程,是如何 產(chǎn)生如圖 9-2 所示的對角線效果的奸鬓。那么讓我們將這些值從 CGContextDrawLinearGradient 過程的那個參數(shù)中去掉,僅僅像之前一樣傳入 0焙畔。圖9-3 顯示了結(jié)果:

圖 9-3 不使用拉伸顏色(stretched colors)的軸向漸變

很容易得出結(jié)論,圖 9-2 和圖 9-3 中使用的是相同的漸變。但是,圖 9-2 中的漸 變擴(kuò)展了起點(diǎn)和終點(diǎn)的顏色,使?jié)u變充滿整個圖形環(huán)境,這就是為什么你能看見整個屏幕被顏色所覆蓋串远。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宏多,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子澡罚,更是在濱河造成了極大的恐慌伸但,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件留搔,死亡現(xiàn)場離奇詭異更胖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)隔显,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門函喉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荣月,你說我怎么就攤上這事∈岜校” “怎么了哺窄?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長账锹。 經(jīng)常有香客問我萌业,道長,這世上最難降的妖魔是什么奸柬? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任生年,我火速辦了婚禮,結(jié)果婚禮上廓奕,老公的妹妹穿的比我還像新娘抱婉。我一直安慰自己档叔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布蒸绩。 她就那樣靜靜地躺著衙四,像睡著了一般。 火紅的嫁衣襯著肌膚如雪患亿。 梳的紋絲不亂的頭發(fā)上传蹈,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音步藕,去河邊找鬼惦界。 笑死,一個胖子當(dāng)著我的面吹牛咙冗,可吹牛的內(nèi)容都是我干的沾歪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼乞娄,長吁一口氣:“原來是場噩夢啊……” “哼瞬逊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仪或,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤确镊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后范删,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蕾域,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年到旦,在試婚紗的時候發(fā)現(xiàn)自己被綠了旨巷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡添忘,死狀恐怖采呐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搁骑,我是刑警寧澤斧吐,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站仲器,受9級特大地震影響煤率,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乏冀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一蝶糯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辆沦,春花似錦昼捍、人聲如沸识虚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舷礼。三九已至,卻和暖如春郊闯,著一層夾襖步出監(jiān)牢的瞬間妻献,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工团赁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留育拨,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓欢摄,卻偏偏與公主長得像熬丧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怀挠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容