本片文章前三章內容大家比較常用,後面的可能會不那麼常用,前面的基礎內容不想看了可以直接從第4段開始
圓角 conrnerRadius
這個功能還是很常見的,本來不想記了,為了整個系列的完整性,還是囉嗦一下。
CALayer
有一個conrnerRadius
的屬性控制圖層的圓角曲率,預設值為0。這個曲率值預設隻影響背景顏色而不影響背景圖片或者子圖層。可以用過下面的示例看一下。
在Storyboard
中放置兩個白色的view
,每個view
分別有兩個子view
(一個黃色,一個青色),而且都超出了父檢視的邊界:
然後在程式碼中寫入如下程式碼:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *leftView;
@property (weak, nonatomic) IBOutlet UIView *rightView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.leftView.layer.cornerRadius = 20.f;
self.rightView.layer.cornerRadius = 20.f;
self.rightView.layer.masksToBounds = YES;
}
@end
然後執行效果如下:
通過上面的示例可以發現:
- 只設定
cornerRadius
時,預設情況下,隻影響背景顏色,而不影響背景圖片或者子圖層。 - 如果想要擷取這個檢視的圖片和子檢視,需要設定
masksToBounds
為YES
。
圖層邊框 border
CALayer
另外兩個非常有用的屬性borderWidth
和borderColor
。borderWidth
是以點為單位定義邊框粗細的浮點數,預設為0。borderColor
定義了邊框的顏色,預設為黑色。
borderColor
是CGColorRef
型別。
邊框繪製在圖層邊界裡面,在所有子圖層之前。對上面的示例程式碼坐下調整:
- (void)viewDidLoad {
[super viewDidLoad];
self.leftView.layer.cornerRadius = 20.f;
self.rightView.layer.cornerRadius = 20.f;
self.leftView.layer.borderWidth = 5.f;
self.rightView.layer.borderWidth = 5.f;
self.rightView.layer.masksToBounds = YES;
}
執行效果如下:
如上面的示例的結果一樣,邊框並不會把寄宿圖或子圖層的相撞計算出來。而且繪製邊框會顯示在最上層。
陰影 shadow
陰影屬性
控制圖層陰影的屬性會比前面的邊框多一些。
-
shadowOpacity
屬性控制陰影透明度的,它是一個在0.0
和1.0
之間的浮點數,如果設定為1.0
將會顯示一個輕微模糊的陰影。 -
shadowColor
屬性控制著陰影的顏色,和borderColor
一樣,它的型別也是CGColorRef
,陰影預設是黑色的。 -
shadowOffset
屬性控制陰影的方向和距離,它是一個CGSize
的值,寬度控制陰影橫向位移,高度控制縱向位移。預設值是`{0, -3}。 -
shadowRadius
屬性控制著陰影的模糊度,當值為0
的時候,陰影和檢視一樣有一個明顯的邊界,值越大,邊界線看起來就會越模糊。
陰影裁剪
和圖層邊框不同,圖層的陰影繼承自內容的外形,而不是根據邊界來界定。
有個頭疼的限制,陰影通常在Layer
的邊界之外,如果我們開啟了maskToBounds
之後,所有突出圖層外的內容都會被裁剪到,包括我們設定的陰影。
之前有個UI需求,同時設定陰影和圓角,圓角簡單的使用layer.cornerRadius
和maskToBounds
。導致一直顯示不出來陰影,差的原因是無法共存,到現在才發現是maskToBounds
導致的。
maskToBounds
把陰影也裁掉的結果肯定不是我們想要的。但是在同一個圖層缺又存在這個問題,所以這看起來很簡單的效果,我們需要用到兩個圖層,一個圖層做maskToBounds
裁剪,一個圖層畫陰影。
下面我們用圓角下面的那個demo稍作修改做個示例:
首先對rightView
做一個透明色的shadowView
的包裹,用來設定陰影:
將程式碼修改成如下:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *leftView;
@property (weak, nonatomic) IBOutlet UIView *rightView;
@property (weak, nonatomic) IBOutlet UIView *shadowView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.leftView.layer.cornerRadius = 20.f;
self.rightView.layer.cornerRadius = 20.f;
self.leftView.layer.borderWidth = 5.f;
self.rightView.layer.borderWidth = 5.f;
// 不新增其他圖層直接給 leftView 設定陰影試一試
self.leftView.layer.shadowOffset = CGSizeMake(5, 5);
self.leftView.layer.shadowOpacity = 0.8f;
self.leftView.layer.shadowRadius = 5.f;
// 給 shadowView 新增和 leftview 一樣的陰影效果
self.shadowView.layer.shadowOffset = CGSizeMake(5, 5);
self.shadowView.layer.shadowOpacity = 0.8f;
self.shadowView.layer.shadowRadius = 5.f;
// 將view裁剪
self.rightView.layer.masksToBounds = YES;
}
@end
執行的效果如圖:
注意看左邊檢視的陰影範圍,很好的說明了圖層的陰影繼承自內容的外形,而不是根據邊界來界定。
shadowPath屬性
圖層陰影並不總是方的,而是從內容的形狀繼承來的。因為計算陰影是一個很耗資源的步驟,尤其有多個子圖層的時候。如果我們指定陰影的形狀的話,可以指定陰影的樣子來節省計算陰影的資源開銷。shadowPath
就是做這個事的,它是一個CGPathRef
型別(指向CGPath
的指標)。
下圖展示了同意寄宿圖不同陰影的設定:
上圖的程式碼實現:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *layerView1;
@property (weak, nonatomic) IBOutlet UIImageView *layerView2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.layerView1.layer.shadowOpacity = 0.5f;
self.layerView2.layer.shadowOpacity = 0.5f;
// 新增矩形陰影
CGMutablePathRef squarePath = CGPathCreateMutable();
CGPathAddRect(squarePath, NULL, self.layerView1.bounds);
self.layerView1.layer.shadowPath = squarePath;
CGPathRelease(squarePath);
// 新增圓形陰影
CGMutablePathRef circlePath = CGPathCreateMutable();
CGPathAddEllipseInRect(circlePath, NULL, self.layerView2.bounds);
self.layerView2.layer.shadowPath = circlePath;
CGPathRelease(circlePath);
}
@end
如果一個矩形或者圓形,用CGPath
很簡單可以實現,如果複雜的圖就需要藉助UIBezierPath
來實現了。
圖層蒙版 mask
這節的原文章前有一堆鋪墊的,我就不說了,想看的點選檢視原文
本節主要是介紹CALayer
的maskt
屬性,它可以實現一些比較好玩的裁剪效果。而不是常規的圓形、矩形裁剪。mask
圖層的Color
屬性無關緊要,它真正有用的是圖層的輪廓。如下圖所示一樣,mask
屬性像是一個切割機,mask
圖層實心的地方會被保留,其他地方被拋棄。
下面我們實現以下上圖的效果,首先在Storyboard
裡建立一個UIImageView
,然後程式碼如下:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 建立mask
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = self.imageView.bounds;
UIImage *maskImage = [UIImage imageNamed:@"test_mask"];
maskLayer.contents = (__bridge id)maskImage.CGImage;
// 新增到 imageview 上
self.imageView.layer.mask = maskLayer;
// 兩張圖片素材是在原文中截圖處理的,所以展示的效果和原文有所差別。
}
@end
執行效果如下:
CALayer
蒙板圖層不侷限於靜態圖,也可以通過程式碼甚至是動畫實時生成蒙板。
拉伸過濾
關於這些我看了原文,確實不懂,而且我也沒碰到過,無從下手做筆記。如果想了解的話請點選此處
組透明 alpha
UIView
有一個alpha
屬性來決定檢視的透明度,對應的CALayer
有一個opacity
屬性。這兩個屬性都會影響子層級的顯示透明度。
下面做個示例。現在Storyboard
裡放置兩個UIButton
。然後程式碼如下:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *leftBtn;
@property (weak, nonatomic) IBOutlet UIButton *rightBtn;
@end
@implementation ViewController
- (UILabel *)subLbl {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 90, 40)];
label.text = @"Hello World";
label.backgroundColor = [UIColor whiteColor];
label.textAlignment = NSTextAlignmentCenter;
return label;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.leftBtn addSubview:[self subLbl]];
[self.rightBtn addSubview:[self subLbl]];
self.rightBtn.alpha = 0.5;
}
@end
執行的效果如下:
這顯示的效果有點怪。右邊的設定了alpha
為0.5
。但是在UILabel
的位置好像不是0.5
的效果。這是因為透明度的混合疊加造成的。實際上右側中間的透明度是0.75
。
當顯示一個50%透明度的圖層時,圖層的每個畫素都會一半顯示自己的顏色,另一半顯示圖層下面的顏色。這是正常的透明度的表現。但是如果圖層包含一個同樣顯示50%透明的子圖層時,你所看到的檢視,50%來自子檢視,25%來了圖層本身的顏色,另外的25%則來自背景色。
如果想保持透明度一直。我們可以在info.plist
檔案中新增UIViewGroupOpacity
並設定成YES
來打到這個效果。還有一種方法就是對CALayer
進行設定。shouldRasterize
屬性可以實現組透明,如果設定成YES
,圖層和它的子圖層會被合成一個整體圖片。
啟用shouldRasterize
屬性,一般需要同時設定圖層的rasterizationScale
屬性防止出現Retina螢幕畫素化的問題。
shouldRasterize和UIViewGroupOpacity一起的時候,會出現效能問題,後面再講。
對上面的展示效果處理的程式碼如下:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *leftBtn;
@property (weak, nonatomic) IBOutlet UIButton *rightBtn;
@end
@implementation ViewController
- (UILabel *)subLbl {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 90, 40)];
label.text = @"Hello World";
label.backgroundColor = [UIColor whiteColor];
label.textAlignment = NSTextAlignmentCenter;
return label;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.leftBtn addSubview:[self subLbl]];
[self.rightBtn addSubview:[self subLbl]];
self.rightBtn.alpha = 0.5;
self.rightBtn.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.rightBtn.layer.shouldRasterize = YES;
}
@end
效果如我們所願。