Core Graphics 101: 陰影和光澤效果

Custom draw the header of this table!
個性化繪製table view的header!

這是結合實例代碼講解Core Graphics API的教程系列的第二部分內容!

在第一部分教程中,我們通過製作美觀的table view cell背景,講解了如何繪製線條,矩形和顏色漸變效果。

在這篇教程中,我們將繼續個性化table view的header。同時,我們將鞏固一些已經學會的知識,進一步學習陰影和光澤效果!

如果你還沒有上篇教程的例子代碼,可以在這裏下載

開始

個性化我們的table view的下一步是美化table view 的header。開始前,讓我們先按照之前的方式做好初步設置 – 製作一個紅色的view,設置爲header的view。

確保”Groups & Files”下面的”Classes”分組被選中,前往菜單的“FileNew File…”,選擇 iOSCocoa Touch Class, Objective-C class,確保”Subclass of UIView”選項被選中,然後點擊下一步。命名文件爲 ”CustomHeader.m”,確保 ”Also create CustomHeader.h”被選中,然後點擊 ”Finish”。

切換到CustomHeader.m文件,取消drawRect方法的註釋,用以下代碼替換掉原來的內容:

CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef redColor = [UIColor colorWithRed:1.0 green:0.0 
    blue:0.0 alpha:1.0].CGColor;
 
CGContextSetFillColorWithColor(context, redColor);
CGContextFillRect(context, self.bounds);

你應該已經對這些操作很熟悉了,因爲我們之前做過!

現在讓我們設置它爲header的view吧。切換到RootViewController.m文件,根據以下代碼做修改:

// In import section
#import "CustomHeader.h"
// Add new methods
- (UIView *) tableView:(UITableView *)tableView 
    viewForHeaderInSection:(NSInteger)section {
    CustomHeader *header = [[[CustomHeader alloc] init] autorelease];        
    return header;
}
-(CGFloat) tableView:(UITableView *)tableView 
    heightForHeaderInSection:(NSInteger)section {
    return 50;
}

實現viewForHeaderInSection方法就可以替換掉table view的header view。我們還需要實現heightForHeaderInSection方法來讓table view知道header的高度。

編譯運行工程,如果一切運行正常,你將看到以下畫面:

Placeholders for Custom Header

很好 – 現在我們來用漂亮的view去填充這個區域吧!

目標

先刷新下內存空間。以下是我們想要的header view的放大效果:

Zoomed in view of Header

注意以下對上述效果的描述:

  • 藍色區域是從淺藍色到深藍色的漸變。
  • 藍色區域的頂部有光澤的效果。
  • 在藍色區域的邊界處有深藍色的邊。
  • 在方框的底部有微小的陰影效果。
  • 頁面的頂部區域已經繪製好了,引入到了頂部cell。
  • Table view controller需要爲每個view設置顏色和label標籤。

以上內容是對上篇教程和Custom UIViews教程的很好複習,但是我們會在接下來講解一些新內容!

開始動手

首先要做的是一些基本設置。我們需要讓table view可以設置header的顏色和顯示一些文本,然後設置一些矩形以便我們在上面繪製(比如header bar還有頁面的下拉效果)。

現在我們開始動手。根據以下代碼對CustomHeader.h文件進行修改:

// Inside @interface
UILabel *_titleLabel;
UIColor *_lightColor;
UIColor *_darkColor;
CGRect _coloredBoxRect;
CGRect _paperRect;
// After @interface
@property (retain) UILabel *titleLabel;
@property (retain) UIColor *lightColor;
@property (retain) UIColor *darkColor;

然後根據以下代碼內容對CustomHeader.m文件進行修改:

// In import section
#import "Common.h"
// After @implementation
@synthesize titleLabel = _titleLabel;
@synthesize lightColor = _lightColor;
@synthesize darkColor = _darkColor;
// Add new methods
- (id)init {
    if ((self = [super init])) {
        self.backgroundColor = [UIColor clearColor];
        self.opaque = NO;
        self.titleLabel = [[[UILabel alloc] init] autorelease];
        _titleLabel.textAlignment = UITextAlignmentCenter;
        _titleLabel.opaque = NO;
        _titleLabel.backgroundColor = [UIColor clearColor];
        _titleLabel.font = [UIFont boldSystemFontOfSize:20.0];
        _titleLabel.textColor = [UIColor whiteColor];
        _titleLabel.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
        _titleLabel.shadowOffset = CGSizeMake(0, -1);
        [self addSubview:_titleLabel];
        self.lightColor = [UIColor colorWithRed:105.0f/255.0f green:179.0f/255.0f 
            blue:216.0f/255.0f alpha:1.0];
        self.darkColor = [UIColor colorWithRed:21.0/255.0 green:92.0/255.0 
            blue:136.0/255.0 alpha:1.0];        
    }
    return self;
}
-(void) layoutSubviews {
 
    CGFloat coloredBoxMargin = 6.0;
    CGFloat coloredBoxHeight = 40.0;
    _coloredBoxRect = CGRectMake(coloredBoxMargin, 
                                 coloredBoxMargin, 
                                 self.bounds.size.width-coloredBoxMargin*2, 
                                 coloredBoxHeight);
 
    CGFloat paperMargin = 9.0;
    _paperRect = CGRectMake(paperMargin, 
                            CGRectGetMaxY(_coloredBoxRect), 
                            self.bounds.size.width-paperMargin*2, 
                            self.bounds.size.height-CGRectGetMaxY(_coloredBoxRect));
 
    _titleLabel.frame = _coloredBoxRect;
 
}
// Replace drawRect with the following
- (void)drawRect:(CGRect)rect {
 
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    CGColorRef redColor = [UIColor colorWithRed:1.0 green:0.0 
        blue:0.0 alpha:1.0].CGColor;
    CGColorRef greenColor = [UIColor colorWithRed:0.0 green:1.0 
        blue:0.0 alpha:1.0].CGColor;
 
    CGContextSetFillColorWithColor(context, redColor);
    CGContextFillRect(context, _coloredBoxRect);
 
    CGContextSetFillColorWithColor(context, greenColor);
    CGContextFillRect(context, _paperRect);
}
// Inside dealloc
[_titleLabel release];
_titleLabel = nil;
[_lightColor release];
_lightColor = nil;
[_darkColor release];
_darkColor = nil;

在之前的教程基礎上,上面的內容你都應該覺得很熟悉。我們快速過一遍。

在init方法中,我們用一些基本的數值建立了label標籤。注意到我們給文本的頂部設置了一塊微小的陰影,讓它看起來像鋸齒狀。原因:如果光線在頂部,然後有些鋸齒效果,就會在上部區域有陰影了。

我們也要確保視圖是透明的,因爲我們想讓背景可以露出來一點。

當我們的view改變大小時就會調用layoutSubviews方法。這裏是我們要添加代碼去計算需要繪製顏色方框的矩形尺寸,以及繪製頁面位置的地方。每一項都是很直接的計算過程。

最後,我們在drawRect函數中使用不同的顏色去填充每一個方框區域,以確保我們計算正確。

使用以下代碼對RootViewController.m文件進行修改:

// Inside tableView:viewForHeaderInSection, before return
header.titleLabel.text = [self tableView:tableView titleForHeaderInSection:section];

編譯運行程序,然後如果運作正常,你會看到以下畫面:
Header colored with subrects to draw

我們又離目標更進一步了!正如你看到的,用顏色填充矩形是一個方便的調試工具!

繪製陰影

讓我們從基礎開始講解繪製的原理吧。我們會先繪製一個頁面,然後是陰影,最後給方框盒子填充顏色。

繪製頁面區域很簡單 – 我們只要給方框填充上白色。

但是我們怎麼去繪製陰影?好的,在Core Graphics裏面,繪製陰影,你只需要調用一個函數去啓用陰影繪製功能,然後繪製軌跡。根據你設定好的參數,陰影將被繪製在軌跡下面 – 這裏不用管軌跡的形狀!

讓我們看看這是怎麼工作的。使用以下代碼,替換掉drawRect的方法:

CGContextRef context = UIGraphicsGetCurrentContext();    
CGColorRef whiteColor = [UIColor colorWithRed:1.0 green:1.0 
    blue:1.0 alpha:1.0].CGColor;
CGColorRef lightColor = _lightColor.CGColor;
CGColorRef darkColor = _darkColor.CGColor;
CGColorRef shadowColor = [UIColor colorWithRed:0.2 green:0.2 
    blue:0.2 alpha:0.5].CGColor;   
 
CGContextSetFillColorWithColor(context, whiteColor);
CGContextFillRect(context, _paperRect);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 2), 3.0, shadowColor);
CGContextSetFillColorWithColor(context, lightColor);
CGContextFillRect(context, _coloredBoxRect);
CGContextRestoreGState(context);

完成顏色和頁面方框的繪製(你現在應該能夠理解了)後,我們調用 CGContextSetShadowWithColor函數去進行陰影繪製。

第一個參數是陰影繪製時的offset(偏移量)值。這裏我們在當前的軌跡下面繪製了兩個點。

接下來的參數是陰影的blur(模糊度)值。o的值將會是顏色較深的邊,值越大,邊的顏色會越柔和。我們把值設定爲3,使邊的顏色變得柔和。

最後我們來設定陰影的顏色。注意到我們使用了灰色,它的alpha值爲0.5(半透明),這讓陰影變得更加逼真。

建立好陰影后,我們使用淺顏色來填充上色了的盒子方框。給軌跡填充顏色的操作,會體現在陰影上面。

最後的修改 – 既然我們使用了提供給我們的顏色,那現在修改RootViewController.m文件去改變section 1的顏色:

// Inside tableView:viewForHeaderInSection, before return statement
if (section == 1) {
    header.lightColor = [UIColor colorWithRed:147.0/255.0 green:105.0/255.0 
        blue:216.0/255.0 alpha:1.0];
    header.darkColor = [UIColor colorWithRed:72.0/255.0 green:22.0/255.0 
        blue:137.0/255.0 alpha:1.0];
}

編譯運行工程,你將看到以下畫面:
Headers with Shadow

Wow – 看起來已經相當漂亮了!現在讓我們用顏色漸變,光澤效果對它去做進一步的修飾吧。

添加光澤效果

在Core Graphics中添加光澤效果到按鈕上面,操作會相當複雜 – 如果你感受到了難度,看看 Matt Gallagher和 Michael Heyeck在這方面的突出貢獻吧。

以我的看法,你可以通過使用一個漸變的alpha mask, 獲得一種相當美觀和接近的光澤效果,這會更加容易理解和編寫代碼,我們接下來會實現它。

下面的代碼我們接下來還會用到,現在把代碼添加到”Common.h”文件中:

void drawGlossAndGradient(CGContextRef context, CGRect rect, CGColorRef startColor, 
    CGColorRef endColor);

添加以下代碼到Common.m文件中:

void drawGlossAndGradient(CGContextRef context, CGRect rect, CGColorRef startColor, 
    CGColorRef endColor) {
 
    drawLinearGradient(context, rect, startColor, endColor);
 
    CGColorRef glossColor1 = [UIColor colorWithRed:1.0 green:1.0 
        blue:1.0 alpha:0.35].CGColor;
    CGColorRef glossColor2 = [UIColor colorWithRed:1.0 green:1.0 
        blue:1.0 alpha:0.1].CGColor;
 
    CGRect topHalf = CGRectMake(rect.origin.x, rect.origin.y, 
        rect.size.width, rect.size.height/2);
 
    drawLinearGradient(context, topHalf, glossColor1, glossColor2);
 
}
好的,以上的代碼負責在矩形上實現顏色漸變效果,然後添加光澤效果到頂部中間。

繪製漸變效果,我們調用之前寫好的代碼。

然後繪製光澤效果,我們只需要在它上面繪製另一層漸變效果,從相當透明(0.35alpha值的白色)到非常透明(0.1alpha值的白色)。

相當簡單吧?我們把代碼添加進去看下效果。回到CustomHeader.m文件,然後添加以下代碼到drawRect:函數的底部:

drawGlossAndGradient(context, _coloredBoxRect, lightColor, darkColor);  
// Draw stroke
CGContextSetStrokeColorWithColor(context, darkColor);
CGContextSetLineWidth(context, 1.0);    
CGContextStrokeRect(context, rectFor1PxStroke(_coloredBoxRect));

這裏我們使用新的程序代碼,然後在方框周圍繪製一個1像素點的深色筆畫,正如在之前教程中學到的那樣。編譯運行程序,然後查看運行結果:
Headers with Gloss

我想現在已經相當不錯了,你覺得呢?

現在還可以做什麼?

這個是本教程的工程代碼,你可以到這裏下載

到現在你應該很渴望去用Core Graphics繪製你自己的東西了 – 想想看你已經能做多少了!

還有個好消息 – 更多的內容將會在本教程系列中出現!在下一篇教程中,我們將用footer去完善好這個很酷的table view,你將在這個過程中學到如何使用Core Graphics去繪製弧線,並且做些收尾工作!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章