update: 2012/01/06
reference: Introduction to CALayers Tutorial | Ray Wenderlich
A. 什麼是 CALayers ?
1. CALayers 只是一個用來在螢幕上描繪可視內容的矩形之類別, 沒錯這也是 UIViews
做的事. 但是這只是一個手法: 每一個 UIView 所畫的內容, 都包含了一個 root layer!
你可以從以下的 code 來存取這個 layer(預設已建好的):
CALayer *myLayer = myView.layer;
2. CALayer 類別的好處是: 它包含了一大堆可以設定的屬性讓你用來改變可見的外觀,
例如:
a. 圖層(layer) 的大小與位置.
b. 圖層的背景顏色.
c. 圖層的內容(圖像或用 Core Graphics 繪製的內容).
d. 圖層的轉角是否使用圓形的.
e. 為圖層設定陰影.
f. 為圖層設定邊框.
g. 其它等等.
---------------------------------------------------------------------------------------------
B. 開始新專案
1. Xcode > File > New > New Project... >
iOS > Application > Single View Application > Next >
Product Name: LayerFun
Device Family: iPhone
Used Storyboard: checked
Used Automatic Reference Couting: checked
> Next
裡. 接著修改 ViewController.m 檔案如下:
// Import QuartzCore.h at the top of the file
#import <QuartzCore/QuartzCore.h>
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//@add
// set the layer's background color to orange
self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;
// round the corners a bit by setting the corner radius
self.view.layer.cornerRadius = 20.0;
// shrink the frame a bit so it's easier to see (not work here)
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
}
3. 編譯並執行:
C. CALayers 與 Sublayers
1. 就像 UIViews 可以有 subviews, CALayers 也可以有 sublayers. 你可以建立一個新的
CALayer 如下所示:
CALayer *sublayer = [CALayer layer];
2. 一旦有了 CALayer, 就可以設定任何你想要的屬性. 記住, 有一項屬性必須明確地設定:
它的 frame (或 bounds/position). 接著, 便可以將新的 layer 當成其它 layer 的
sublayer:
[myLayer addSublayer:sublayer];
3. 在 ViewController.m 檔案裡的 viewDidLoad 方法裡, 替目前 view 的 layer 加入一個
sublayer :
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//@add
self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;
self.view.layer.cornerRadius = 20.0;
self.view.layer.frame = CGRectInset(self.view.layer.frame, 80, 80);
//@add sublayer
CALayer *sublayer = [CALayer layer];
sublayer.backgroundColor = [UIColor blueColor].CGColor;
// set shadows
sublayer.shadowOffset = CGSizeMake(0, 3);
sublayer.shadowRadius = 5.0;
sublayer.shadowColor = [UIColor blackColor].CGColor;
sublayer.shadowOpacity = 0.8;
// sets the frame
sublayer.frame = CGRectMake(30, 30, 128, 192);
[self.view.layer addSublayer:sublayer];
}
說明: 這些座標是相對於 parent layer's frame 的.
4. 編譯並執行:
D. 設定 CALayer 影像內容
1. 在 ViewController.m 檔案裡的 viewDidLoad 方法裡, 在 addSublayer: 之前,
加入以下的 code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
....
//@add
sublayer.contents = (id) [UIImage imageNamed:@"BattleMapSplashScreen.jpg"].CGImage;
sublayer.borderColor = [UIColor blackColor].CGColor;
sublayer.borderWidth = 2.0;
[self.view.layer addSublayer:sublayer];
}
2. 編譯並執行:
---------------------------------------------------------------------------------------------
E. 圓角與影像內容
1. 如果在有影像內容的 CALayer 設定 cornerRadius, 影像仍然會畫超出圓角的範圍.
可以藉由設定 sublayer.masksToBounds = YES 來解決, 但是如此一來陰影就會被
遮罩屏除, 而無法顯示.
2. 解決辦法之一, 建立二個 layers. 外部的 layer 僅僅只是一個著色的 CALayer 並賦予
外框與陰影. 內部的 layer 包含影像, 並且為圓角的遮罩. 如此一來, 外部的 layer 能夠
畫出陰影, 並且內部的 layer 包含著影像.
3. 在 ViewController.m 檔案裡的 viewDidLoad 方法裡, 修改以下的 code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//@add
self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;
self.view.layer.cornerRadius = 20.0;
self.view.layer.frame = CGRectInset(self.view.layer.frame, 80, 80);
CALayer *sublayer = [CALayer layer];
sublayer.backgroundColor = [UIColor blueColor].CGColor;
sublayer.shadowOffset = CGSizeMake(0, 3);
sublayer.shadowRadius = 5.0;
sublayer.shadowColor = [UIColor blackColor].CGColor;
sublayer.shadowOpacity = 0.8;
sublayer.frame = CGRectMake(30, 30, 128, 192);
sublayer.borderColor = [UIColor blackColor].CGColor;
sublayer.borderWidth = 2.0;
sublayer.cornerRadius = 10.0;
[self.view.layer addSublayer:sublayer];
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = sublayer.bounds;
imageLayer.cornerRadius = 10.0;
imageLayer.contents = (id) [UIImage imageNamed:@"BattleMapSplashScreen.jpg"].CGImage;
imageLayer.masksToBounds = YES;
[sublayer addSublayer:imageLayer];
}
4. 編譯並執行:
F. CALayer 與客製化繪圖內容
1. 如何利用 Core Graphics 來客製化繪製 layer 的內容:
概念是: 將一個類別設為 layer 的 delegate, 並且該類別必需實作
drawLayer:inContext 方法. 這樣便能夠在該方法內包含任何 Core Graphics 繪製
的 code.
2. 現在試著做: 新增一個 layer 並且在其內繪製一個圖案. 你將會需要把 view controller
設為 layer 的 delegate, 並且實作 drawLayer:inContext 方法來繪製圖案. 繪製圖案的
code 將會使用到與 Core Graphics 101: Patterns 相同的 code.
3. 在 ViewController.m 檔案裡的 viewDidLoad 方法裡, 新增以下的 code:
- (void)viewDidLoad
{
[super viewDidLoad];
....
//@add
CALayer *customDrawn = [CALayer layer];
customDrawn.delegate = self;
customDrawn.backgroundColor = [UIColor greenColor].CGColor;
customDrawn.frame = CGRectMake(30, 250, 128, 40);
customDrawn.shadowOffset = CGSizeMake(0, 3);
customDrawn.shadowRadius = 5.0;
customDrawn.shadowColor = [UIColor blackColor].CGColor;
customDrawn.shadowOpacity = 0.8;
customDrawn.cornerRadius = 10.0;
customDrawn.borderColor = [UIColor blackColor].CGColor;
customDrawn.borderWidth = 2.0;
customDrawn.masksToBounds = YES;
[self.view.layer addSublayer:customDrawn];
[customDrawn setNeedsDisplay];
}
說明: a. customDrawn 這個 layer 將 delegate 設為 self (view controller). 意謂著,
self (view controller) 必需去實作 drawLayer:inContext 這個方法來繪製
layer 的內容.
b. 新增 layer 之後, 必需藉由呼叫 setNeedsDisplay 方法來告訴 layer 要去
refresh 自己本身(並且呼叫 drawLayer:inContext) , 否則
drawLayer:inContext 將不會被呼叫到.
4. 在 ViewController.h 檔案中, 新增以下的 code, 用來宣告繪製圖案的方法:
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
}
//@add
void MyDrawColoredPattern (void *info, CGContextRef context);
@end
5. 在 ViewController.m 檔案中, 新增以下的 code, 用來定義繪製圖案方法的內容與
實作 drawLayer:inContext 方法:
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
//@add: 如果 Xcode 出現: Implicit declaration of function 'radians' is invalid in C99
static inline double radians (double degrees) {return degrees * M_PI/180;}
....
void MyDrawColoredPattern (void *info, CGContextRef context) {
CGColorRef dotColor = [UIColor colorWithHue:0 saturation:0 brightness:0.07 alpha:1.0].CGColor;
CGColorRef shadowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.1].CGColor;
CGContextSetFillColorWithColor(context, dotColor);
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1, shadowColor);
CGContextAddArc(context, 3, 3, 4, 0, radians(360), 0);
CGContextFillPath(context);
CGContextAddArc(context, 16, 16, 4, 0, radians(360), 0);
CGContextFillPath(context);
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context { CGColorRef bgColor = [UIColor colorWithHue:0.6 saturation:1.0 brightness:1.0 alpha:1.0].CGColor; CGContextSetFillColorWithColor(context, bgColor); CGContextFillRect(context, layer.bounds); static const CGPatternCallbacks callbacks = { 0, &MyDrawColoredPattern, NULL }; CGContextSaveGState(context); CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL); CGContextSetFillColorSpace(context, patternSpace); CGColorSpaceRelease(patternSpace); CGPatternRef pattern = CGPatternCreate(NULL, layer.bounds, CGAffineTransformIdentity, 24, 24, kCGPatternTilingConstantSpacing, true, &callbacks); CGFloat alpha = 1.0; CGContextSetFillPattern(context, pattern, &alpha); CGPatternRelease(pattern); CGContextFillRect(context, layer.bounds); CGContextRestoreGState(context); }
6. 編譯並執行:
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。