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

最近在看《iOS 6 Programming Cookbook》的翻譯版,它由DevDiv論壇的網(wǎng)友翻譯,原文地址:點擊跳轉(zhuǎn)。由于下載的pdf都有水印,并且排版不是很好,特別是代碼排版,基本不能看,所以這里就整理了一下,方便再次查看。另外把里面提到的點寫了一個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 值越大,形狀右邊的陰影就擴散得越遠(yuǎn)。位移的 y 值越大,下部的陰影就越低;
  3. 陰影的模糊值,以浮點值(CGFloat)來指定。指定 0.0f 將導(dǎo)致陰影成為固態(tài)形狀。這個 值越高,陰影就越模糊。
CGContextSetShadowWithColor 

這個方法接受的參數(shù)和 CGContextSetShadow 完全相同,不過加了一個 CGColorRef 類型的參數(shù),用于設(shè)置陰影的顏色。

在此前我 到,圖形環(huá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é)束于綠色的軸向漸變

這里只討論軸向漸變。軸向漸變是以一種顏色為起點,而以另一種顏色為終點的漸變(雖然可以在起點和終點用一種顏色,但是那不會產(chǎn)生漸變)?!拜S向”的意思是和某個軸有關(guā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ù)量。 所以,如果你請求兩個位置(起點和終點),你就必須在數(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 個)。中間的兩個顏色的值決定了在起點和終點間如何插入顏色。

  • 位置數(shù)量
    指定我們想要有多少種顏色和位置。

我們來看例子。假設(shè)我們想繪制如圖 圖9-1 所示的漸變,下面是步驟:

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

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

  1. 根據(jù)圖 9-1 所選擇的顏色,我們選擇藍(lán)色作為起點(左邊),綠色作為終點(右邊)。我選擇的變量名(startColorComponentsendColorComponents)是特意選定來幫助我們記住我們對每個顏色在做的事情。實際上我們會使用數(shù)組位置來指定起點和終點:
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)建這個 漸變對象。
  • 起點
    圖形環(huán)境中的一點,由 CGPoint 指定,它指定了漸變的起點。
  • 終點
    圖形環(huán)境中的一點,由 CGPoint 指定,它指定了漸變的終點。
  • 漸變繪制選項
    指定了當(dāng)你指定的起點或終點不是圖形環(huán)境的邊緣時的行為。你可以使用你的開始色或
    結(jié)束色來填充漸變之外的區(qū)域。指定下列參數(shù)值之一:
 kCGGradientDrawsAfterEndLocation
  在漸變終點之后將漸變擴展到所有點
kCGGradientDrawsBeforeStartLocation
  在漸變起點之前將漸變擴展到所有點
 0
  不會以任何方式擴展?jié)u變

為了在兩端都擴展顏色,可以用邏輯 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 所示。因為我們從最左邊的點開始將漸變拖拽到最右邊的點,所以我們不能利用 CGContextDrawLinearGradient 過程最后的“漸變繪制選項”參數(shù)。 讓我們修改一下吧?繪制一個如圖 9-2 所示的漸變?nèi)绾?

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

我們會使用這部分早先用過的相同的代碼來得到結(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 中的漸 變擴展了起點和終點的顏色,使?jié)u變充滿整個圖形環(huán)境,這就是為什么你能看見整個屏幕被顏色所覆蓋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容