目錄
- 圓角
- 圖層邊框
- 陰影
- 圖層蒙版
- 拉伸過(guò)濾
- 組透明
- 總結(jié)
一 圓角
CALayer
有一個(gè)叫做conrnerRadius
的屬性控制著圖層角的曲率陪汽。它是一個(gè)浮點(diǎn)數(shù)挚冤,默認(rèn)為0(為0的時(shí)候就是直角),但是你可以把它設(shè)置成任意值舍哄。默認(rèn)情況下,這個(gè)曲率值只影響背景顏色
而不影響背景圖片
或是子圖層
蟆沫。不過(guò)饭庞,如果把masksToBounds
設(shè)置成YES的話绸狐,圖層里面的所有東西都會(huì)被截取。
然后在代碼中符相,我們?cè)O(shè)置角的半徑為20個(gè)點(diǎn)啊终,并裁剪掉第一個(gè)視圖的超出部分。
- (void)drawUI {
UIView *grayView1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
grayView1.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView1];
UIView *redView1 = [[UIView alloc] initWithFrame:CGRectMake(-30, -30, 60, 60)];
redView1.backgroundColor = [UIColor redColor];
[grayView1 addSubview:redView1];
UIView *grayView2 = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 100, 100)];
grayView2.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView2];
UIView *redView2 = [[UIView alloc] initWithFrame:CGRectMake(-30, -30, 60, 60)];
redView2.backgroundColor = [UIColor redColor];
[grayView2 addSubview:redView2];
}
- 運(yùn)行結(jié)果如下
設(shè)置cornerRadius和masksToBounds
grayView1.layer.cornerRadius = 20;
grayView2.layer.cornerRadius = 20;
grayView2.layer.masksToBounds = YES;
- 運(yùn)行結(jié)果如下
下圖中散怖,紅色的子視圖沿角半徑被裁剪了咬最。下邊的子視圖沿邊界被裁剪了。
單獨(dú)控制每個(gè)層的圓角曲率也不是不可能的翅雏。如果想創(chuàng)建有些圓角有些直角的圖層或視圖時(shí)望几,你可能需要一些不同的方法靴迫。比如使用一個(gè)圖層蒙板
或者是CAShapeLayer
玉锌。
二 圖層邊框
CALayer另外兩個(gè)非常有用屬性就是borderWidth
和borderColor
。二者共同定義了圖層邊的繪制樣式榄融。這條線(也被稱作stroke
)沿著圖層的bounds
繪制黄刚,同時(shí)也包含圖層的角。
borderWidth
是以點(diǎn)為單位的定義邊框粗細(xì)的浮點(diǎn)數(shù)畏邢,默認(rèn)為0程储。borderColor
定義了邊框的顏色章鲤,默認(rèn)為黑色。
borderColor
是CGColorRef類型皱蹦,而不是UIColor沪哺,所以它不是Cocoa的內(nèi)置對(duì)象。不過(guò)呢柔袁,你肯定也清楚圖層引用了borderColor捶索,雖然屬性聲明并不能證明這一點(diǎn)。CGColorRef在引用/釋放時(shí)候的行為表現(xiàn)得與NSObject極其相似燎竖。但是Objective-C語(yǔ)法并不支持這一做法构回,所以CGColorRef屬性即便是強(qiáng)引用也只能通過(guò)assign關(guān)鍵字來(lái)聲明。
邊框
是繪制在圖層邊界里面的借跪,而且在所有子內(nèi)容之前
,也在子圖層之前卵牍。
// 邊框
grayView1.layer.borderWidth = 5.0;
grayView2.layer.borderWidth = 5.0;
- 運(yùn)行結(jié)果如下
仔細(xì)觀察會(huì)發(fā)現(xiàn)邊框并不會(huì)把寄宿圖或子圖層的形狀計(jì)算進(jìn)來(lái)京腥,如果圖層的子圖層超過(guò)了邊界他宛,或者是寄宿圖在透明區(qū)域有一個(gè)透明蒙板镜撩,邊框仍然會(huì)沿著圖層的邊界繪制出來(lái)宜鸯。
- (void)drawCat {
UIView *catView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
catView.center = self.view.center;
[self.view addSubview:catView];
catView.layer.contents = (__bridge id)[UIImage imageNamed:@"cat"].CGImage;
catView.layer.contentsGravity = kCAGravityResizeAspectFill;
catView.layer.borderWidth = 5.0;
}
- 運(yùn)行結(jié)果如下
三 陰影
iOS的另一個(gè)常見特性呢锯梁,就是陰影剥懒。陰影往往可以達(dá)到圖層深度暗示的效果。也能夠用來(lái)強(qiáng)調(diào)正在顯示的圖層和優(yōu)先級(jí)(比如說(shuō)一個(gè)在其他視圖之前的彈出框)壁却,不過(guò)有時(shí)候他們只是單純的裝飾目的。
給shadowOpacity
屬性一個(gè)大于默認(rèn)值(也就是0)的值盐肃,陰影就可以顯示在任意圖層之下。shadowOpacity
是一個(gè)必須在0.0(不可見)和1.0(完全不透明)
之間的浮點(diǎn)數(shù)谦铃。如果設(shè)置為1.0驹闰,將會(huì)顯示一個(gè)有輕微模糊的黑色陰影稍微在圖層之上。若要改動(dòng)陰影的表現(xiàn),你可以使用CALayer的另外三個(gè)屬性:shadowColor
蓄诽,shadowOffset
和shadowRadius
。
- (void)shadowOpacity {
UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
blueView.backgroundColor = [UIColor blueColor];
blueView.center = self.view.center;
[self.view addSubview:blueView];
blueView.layer.shadowOpacity = 0.5;
}
- 運(yùn)行效果如下
shadowColor
shadowColor
屬性控制著陰影的顏色
,和borderColor
和backgroundColor
一樣嚎莉,它的類型也是CGColorRef
趋箩。陰影默認(rèn)是黑色,大多數(shù)時(shí)候你需要的陰影也是黑色的(其他顏色的陰影看起來(lái)是不是有一點(diǎn)點(diǎn)奇怪)芍锦。
blueView.layer.shadowColor = [UIColor redColor].CGColor;
- 運(yùn)行結(jié)果如下
shadowOffset
shadowOffset
屬性控制著陰影的方向
和距離
竹勉。它是一個(gè)CGSize
的值,寬度控制這陰影橫向的位移娄琉,高度控制著縱向的位移次乓。shadowOffset的默認(rèn)值是{0, -3}
,意即陰影相對(duì)于Y軸
有3個(gè)點(diǎn)的向上位移
孽水。
為什么要默認(rèn)向上的陰影呢票腰?盡管Core Animation
是從圖層套裝演變而來(lái)(可以認(rèn)為是為iOS創(chuàng)建的私有動(dòng)畫框架)缘滥,但是呢,它卻是在Mac OS上面世的备典,前面有提到潮针,二者的Y軸是顛倒的。這就導(dǎo)致了默認(rèn)的3個(gè)點(diǎn)位移的陰影是向上的张症。在Mac上颜价,shadowOffset
的默認(rèn)值是陰影向下的及志,這樣你就能理解為什么iOS上的陰影方向是向上的了冶共。
蘋果更傾向于用戶界面的陰影應(yīng)該是垂直向下的谐岁,所以在iOS把陰影寬度設(shè)為0帅涂,然后高度設(shè)為一個(gè)正值不失為一個(gè)做法。
blueView.layer.shadowOffset = CGSizeMake(10, 10);
- 運(yùn)行結(jié)果如下
shadowRadius
shadowRadius
屬性控制著陰影的模糊度
,當(dāng)它的值是0
的時(shí)候,陰影就和視圖一樣有一個(gè)非常確定的邊界線秕铛。當(dāng)值越來(lái)越大的時(shí)候绽快,邊界線看上去就會(huì)越來(lái)越模糊和自然憾儒。蘋果自家的應(yīng)用設(shè)計(jì)更偏向于自然的陰影艺骂,所以一個(gè)非零值再合適不過(guò)了托嚣。
通常來(lái)講浙垫,如果你想讓視圖或控件非常醒目獨(dú)立于背景之外(比如彈出框遮罩層)士八,你就應(yīng)該給shadowRadius設(shè)置一個(gè)稍大的值。陰影越模糊绊序,圖層的深度看上去就會(huì)更明顯。
blueView.layer.shadowRadius = 50;
- 運(yùn)行結(jié)果如下
3.2 陰影裁剪
和圖層邊框不同靠粪,圖層的陰影繼承自內(nèi)容的外形评姨,而不是根據(jù)邊界和角半徑來(lái)確定。為了計(jì)算出陰影的形狀剃氧,Core Animation會(huì)將寄宿圖(包括子視圖,如果有的話)考慮在內(nèi)缆蝉,然后通過(guò)這些來(lái)完美搭配圖層形狀從而創(chuàng)建一個(gè)陰影。
- (void)drawCat {
UIView *catView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
catView.center = self.view.center;
[self.view addSubview:catView];
catView.layer.contents = (__bridge id)[UIImage imageNamed:@"cat"].CGImage;
catView.layer.contentsGravity = kCAGravityResizeAspectFill;
catView.layer.borderWidth = 5.0;
catView.layer.shadowOpacity = 0.5;
}
- 運(yùn)行效果如下
當(dāng)陰影和裁剪扯上關(guān)系的時(shí)候就有一個(gè)頭疼的限制:陰影通常就是在Layer的邊界之外膳沽,如果你開啟了masksToBounds屬性,所有從圖層中突出來(lái)的內(nèi)容都會(huì)被才剪掉让禀。如果我們?cè)谖覀冎暗倪吙蚴纠?xiàng)目中增加圖層的陰影屬性時(shí)挑社,你就會(huì)發(fā)現(xiàn)問(wèn)題所在。
- (void)drawUI {
UIView *grayView1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
grayView1.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView1];
UIView *redView1 = [[UIView alloc] initWithFrame:CGRectMake(-30, -30, 60, 60)];
redView1.backgroundColor = [UIColor redColor];
[grayView1 addSubview:redView1];
UIView *grayView2 = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 100, 100)];
grayView2.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView2];
UIView *redView2 = [[UIView alloc] initWithFrame:CGRectMake(-30, -30, 60, 60)];
redView2.backgroundColor = [UIColor redColor];
[grayView2 addSubview:redView2];
// cornerRadius圓角
grayView1.layer.cornerRadius = 20;
grayView2.layer.cornerRadius = 20;
grayView2.layer.masksToBounds = YES;
// 邊框
grayView1.layer.borderWidth = 5.0;
grayView2.layer.borderWidth = 5.0;
// 陰影
grayView1.layer.shadowOpacity = 0.5;
grayView2.layer.shadowOpacity = 0.5;
}
- 運(yùn)行效果如下
如果你想沿著內(nèi)容裁切巡揍,你需要用到兩個(gè)圖層:一個(gè)只畫陰影的空的外圖層痛阻,和一個(gè)用masksToBounds裁剪內(nèi)容的內(nèi)圖層。
我們只把陰影用在最外層的視圖上腮敌,內(nèi)層視圖進(jìn)行裁剪阱当。
- (void)shadowOpacity1 {
UIView *grayView1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
grayView1.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView1];
UIView *redView1 = [[UIView alloc] initWithFrame:CGRectMake(-30, -30, 60, 60)];
redView1.backgroundColor = [UIColor redColor];
[grayView1 addSubview:redView1];
UIView *grayView2 = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 100, 100)];
grayView2.backgroundColor = [UIColor grayColor];
[self.view addSubview:grayView2];
UIView *redView2 = [[UIView alloc] initWithFrame:CGRectMake(-30, -30, 60, 60)];
redView2.backgroundColor = [UIColor redColor];
[grayView2 addSubview:redView2];
// cornerRadius圓角
grayView1.layer.cornerRadius = 20;
grayView2.layer.cornerRadius = 20;
grayView2.layer.masksToBounds = YES;
// 邊框
grayView1.layer.borderWidth = 5.0;
grayView2.layer.borderWidth = 5.0;
// add a shadow to grayView1
grayView1.layer.shadowOpacity = 0.5;
grayView1.layer.shadowOffset = CGSizeMake(0.5, 0.5);
grayView1.layer.shadowRadius = 5.0;
// add same shadow to shadowView
UIView *shadowView = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 100, 100)];
shadowView.layer.shadowOpacity = 0.5;
shadowView.layer.shadowOffset = CGSizeMake(0.5, 0.5);
shadowView.layer.shadowRadius = 5.0;
[grayView2 insertSubview:shadowView atIndex:0];
}
- 運(yùn)行效果如下
3.3 shadowPath屬性
我們已經(jīng)知道圖層陰影并不總是方的,而是從圖層內(nèi)容的形狀繼承而來(lái)糜工。這看上去不錯(cuò)弊添,但是實(shí)時(shí)計(jì)算陰影也是一個(gè)非常消耗資源的,尤其是圖層有多個(gè)子圖層捌木,每個(gè)圖層還有一個(gè)有透明效果的寄宿圖的時(shí)候油坝。
如果你事先知道你的陰影形狀會(huì)是什么樣子的,你可以通過(guò)指定一個(gè)shadowPath
來(lái)提高性能。shadowPath是一個(gè)CGPathRef類型(一個(gè)指向CGPath的指針)澈圈。CGPath是一個(gè)Core Graphics對(duì)象彬檀,用來(lái)指定任意的一個(gè)矢量圖形。我們可以通過(guò)這個(gè)屬性單獨(dú)于圖層形狀之外指定陰影的形狀瞬女。
- (void)shadowPath {
UIView *grayView1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
grayView1.layer.contents = (__bridge id)[UIImage imageNamed:@"cat"].CGImage;
[self.view addSubview:grayView1];
UIView *grayView2 = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 100, 100)];
grayView2.layer.contents = (__bridge id)[UIImage imageNamed:@"cat"].CGImage;
[self.view addSubview:grayView2];
// enable layer shadows
grayView1.layer.shadowOpacity = 0.5;
grayView2.layer.shadowOpacity = 0.5;
// create a square shadow
CGMutablePathRef squarPath = CGPathCreateMutable();
CGPathAddRect(squarPath, NULL, grayView1.bounds);
grayView1.layer.shadowPath = squarPath;
CGPathRelease(squarPath);
// create a circle shadow
CGMutablePathRef circlePath = CGPathCreateMutable();
CGPathAddEllipseInRect(circlePath, NULL, CGRectMake(-50, -50, 200, 200));
grayView2.layer.shadowPath = circlePath;
CGPathRelease(circlePath);
}
- 運(yùn)行效果如下
如果是一個(gè)矩形或者是圓窍帝,用CGPath會(huì)相當(dāng)簡(jiǎn)單明了。但是如果是更加復(fù)雜一點(diǎn)的圖形拆魏,UIBezierPath類會(huì)更合適盯桦,它是一個(gè)由UIKit提供的在CGPath基礎(chǔ)上的Objective-C包裝類。
四 圖層蒙版
通過(guò)masksToBounds屬性渤刃,我們可以沿邊界裁剪圖形拥峦;通過(guò)cornerRadius屬性,我們還可以設(shè)定一個(gè)圓角卖子。但是有時(shí)候你希望展現(xiàn)的內(nèi)容不是在一個(gè)矩形或圓角矩形略号。比如,你想展示一個(gè)有星形框架的圖片洋闽,又或者想讓一些古卷文字慢慢漸變成背景色玄柠,而不是一個(gè)突兀的邊界。
使用一個(gè)32位有alpha通道的png圖片通常是創(chuàng)建一個(gè)無(wú)矩形視圖最方便的方法诫舅,你可以給它指定一個(gè)透明蒙板來(lái)實(shí)現(xiàn)羽利。但是這個(gè)方法不能讓你以編碼的方式動(dòng)態(tài)地生成蒙板,也不能讓子圖層或子視圖裁剪成同樣的形狀刊懈。
CALayer有一個(gè)屬性叫做mask
可以解決這個(gè)問(wèn)題这弧。這個(gè)屬性本身就是個(gè)CALayer類型,有和其他圖層一樣的繪制和布局屬性虚汛。它類似于一個(gè)子圖層匾浪,相對(duì)于父圖層(即擁有該屬性的圖層)布局,但是它卻不是一個(gè)普通的子圖層卷哩。不同于那些繪制在父圖層中的子圖層蛋辈,mask圖層定義了父圖層的部分可見區(qū)域
。
mask
圖層的Color
屬性是無(wú)關(guān)緊要的将谊,真正重要的是圖層的輪廓冷溶。mask屬性就像是一個(gè)餅干切割機(jī),mask圖層實(shí)心的部分會(huì)被保留下來(lái)瓢娜,其他的則會(huì)被拋棄挂洛。
如果mask
圖層比父圖層要小,只有在mask圖層里面的內(nèi)容才是它關(guān)心的眠砾,除此以外的一切都會(huì)被隱藏起來(lái)虏劲。
- (void)mask {
UIView *catView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
catView.center = self.view.center;
catView.layer.contents = (__bridge id)[UIImage imageNamed:@"cat"].CGImage;
[self.view addSubview:catView];
// create mask layer
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = catView.bounds;
maskLayer.contents = (__bridge id)[UIImage imageNamed:@"aqara_logo_login"].CGImage;
// apply mask to image layer
catView.layer.mask = maskLayer;
}
- 運(yùn)行結(jié)果如下
CALayer蒙板圖層真正厲害的地方在于蒙板圖不局限于靜態(tài)圖托酸。任何有圖層構(gòu)成的都可以作為mask屬性,這意味著你的蒙板可以通過(guò)代碼甚至是動(dòng)畫實(shí)時(shí)生成柒巫。
五 拉伸過(guò)濾
最后我們?cè)賮?lái)談?wù)刴inificationFilter和magnificationFilter屬性励堡。總得來(lái)講堡掏,當(dāng)我們視圖顯示一個(gè)圖片的時(shí)候应结,都應(yīng)該正確地顯示這個(gè)圖片(意即:以正確的比例和正確的1:1像素顯示在屏幕上)。原因如下:
- 能夠顯示最好的畫質(zhì)泉唁,像素既沒有被壓縮也沒有被拉伸
- 能更好的使用內(nèi)存鹅龄,因?yàn)檫@就是所有你要存儲(chǔ)的東西
- 最好的性能表現(xiàn),CPU不需要為此額外的計(jì)算亭畜。
不過(guò)有時(shí)候扮休,顯示一個(gè)非真實(shí)大小的圖片確實(shí)是我們需要的效果。比如說(shuō)一個(gè)頭像或是圖片的縮略圖拴鸵,再比如說(shuō)一個(gè)可以被拖拽和伸縮的大圖玷坠。這些情況下,為同一圖片的不同大小存儲(chǔ)不同的圖片顯得又不切實(shí)際劲藐。
當(dāng)圖片需要顯示不同的大小的時(shí)候八堡,有一種叫做拉伸過(guò)濾的算法就起到作用了。它作用于原圖的像素上并根據(jù)需要生成新的像素顯示在屏幕上聘芜。
事實(shí)上兄渺,重繪圖片大小也沒有一個(gè)統(tǒng)一的通用算法。這取決于需要拉伸的內(nèi)容汰现,放大或是縮小的需求等這些因素溶耘。CALayer為此提供了三種拉伸過(guò)濾方法,他們是:
- kCAFilterLinear
- kCAFilterNearest
- kCAFilterTrilinear
minification
(縮小圖片)和magnification
(放大圖片)默認(rèn)的過(guò)濾器都是kCAFilterLinear
服鹅,這個(gè)過(guò)濾器采用雙線性濾波算法,它在大多數(shù)情況下都表現(xiàn)良好百新。雙線性濾波算法通過(guò)對(duì)多個(gè)像素取樣最終生成新的值企软,得到一個(gè)平滑的表現(xiàn)不錯(cuò)的拉伸。但是當(dāng)放大倍數(shù)比較大的時(shí)候圖片就模糊不清了饭望。
kCAFilterTrilinear
和kCAFilterLinear
非常相似仗哨,大部分情況下二者都看不出來(lái)有什么差別。但是铅辞,較雙線性濾波算法而言厌漂,三線性濾波算法存儲(chǔ)了多個(gè)大小情況下的圖片(也叫多重貼圖),并三維取樣斟珊,同時(shí)結(jié)合大圖和小圖的存儲(chǔ)進(jìn)而得到最后的結(jié)果苇倡。
這個(gè)方法的好處在于算法能夠從一系列已經(jīng)接近于最終大小的圖片中得到想要的結(jié)果,也就是說(shuō)不要對(duì)很多像素同步取樣。這不僅提高了性能旨椒,也避免了小概率因舍入錯(cuò)誤引起的取樣失靈的問(wèn)題晓褪。
對(duì)于大圖來(lái)說(shuō),雙線性濾波和三線性濾波表現(xiàn)得更出色
kCAFilterNearest
是一種比較武斷的方法综慎。從名字不難看出涣仿,這個(gè)算法(也叫最近過(guò)濾)就是取樣最近的單像素點(diǎn)而不管其他的顏色。這樣做非呈揪快好港,也不會(huì)使圖片模糊。但是米罚,最明顯的效果就是钧汹,會(huì)使得壓縮圖片更糟,圖片放大之后也顯得塊狀或是馬賽克嚴(yán)重阔拳。
對(duì)于沒有斜線的小圖來(lái)說(shuō)崭孤,最近過(guò)濾算法要好很多
總的來(lái)說(shuō),對(duì)于比較小的圖或者是差異特別明顯糊肠,極少斜線的大圖辨宠,最近過(guò)濾算法會(huì)保留這種差異明顯的特質(zhì)以呈現(xiàn)更好的結(jié)果。但是對(duì)于大多數(shù)的圖尤其是有很多斜線或是曲線輪廓的圖片來(lái)說(shuō)货裹,最近過(guò)濾算法會(huì)導(dǎo)致更差的結(jié)果嗤形。換句話說(shuō),線性過(guò)濾保留了形狀弧圆,最近過(guò)濾則保留了像素的差異
赋兵。
讓我們來(lái)實(shí)驗(yàn)一下,用LCD風(fēng)格的數(shù)字方式顯示搔预。我們用簡(jiǎn)單的像素字體(一種用像素構(gòu)成字符的字體霹期,而非矢量圖形)創(chuàng)造數(shù)字顯示方式,用圖片存儲(chǔ)起來(lái)拯田。
顯示一個(gè)LCD風(fēng)格的時(shí)鐘
- (void)clock {
// get spritesheet image
UIImage *digits = [UIImage imageNamed:@"digits"];
// add img view
for (int i = 0; i < 6; i++) {
UIView *imgView = [[UIView alloc] initWithFrame:CGRectMake(100 + i * 30, 300, 10, 20)];
[self.digitViews addObject:imgView];
[self.view addSubview:imgView];
}
// setup digit views
for (UIView *imgView in self.digitViews) {
imgView.layer.contents = (__bridge id)digits.CGImage;
imgView.layer.contentsRect = CGRectMake(0, 0, 0.1, 1.0);
imgView.layer.contentsGravity = kCAGravityResizeAspect;
}
// start timer
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf tick];
}];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
[self tick];
}
- (void)tick {
//convert time to hours, minutes and seconds
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
// set hours
[self setDigit:components.hour / 10 forView:self.digitViews[0]];
[self setDigit:components.hour % 10 forView:self.digitViews[1]];
// set minutes
[self setDigit:components.minute / 10 forView:self.digitViews[2]];
[self setDigit:components.minute % 10 forView:self.digitViews[3]];
// set seconds
[self setDigit:components.second / 10 forView:self.digitViews[4]];
[self setDigit:components.second % 10 forView:self.digitViews[5]];
}
- (void)setDigit:(NSInteger)digit forView:(UIView *)view {
view.layer.contentsRect = CGRectMake(digit * 0.1, 0, 0.1, 1.0);
}
- 運(yùn)行效果如下
這樣做的確起了效果历造,但是圖片看起來(lái)模糊了〈樱看起來(lái)默認(rèn)的kCAFilterLinear選項(xiàng)讓我們失望了吭产。
為了顯示清晰,我們需要在for循環(huán)中加入如下代碼:
imgView.layer.magnificationFilter = kCAFilterNearest;
- 運(yùn)行效果如下
六 組透明
UIView有一個(gè)叫做alpha
的屬性來(lái)確定視圖的透明度鸭轮。CALayer有一個(gè)等同的屬性叫做opacity
臣淤,這兩個(gè)屬性都是影響子層級(jí)
的。也就是說(shuō)窃爷,如果你給一個(gè)圖層設(shè)置了opacity
屬性邑蒋,那它的子圖層都會(huì)受此影響
姓蜂。
iOS常見的做法是把一個(gè)控件的alpha值設(shè)置為0.5(50%)以使其看上去呈現(xiàn)為不可用狀態(tài)。對(duì)于獨(dú)立的視圖來(lái)說(shuō)還不錯(cuò)寺董,但是當(dāng)一個(gè)控件有子視圖的時(shí)候就有點(diǎn)奇怪了覆糟,圖4.20展示了一個(gè)內(nèi)嵌了UILabel的自定義UIButton;左邊是一個(gè)不透明的按鈕遮咖,右邊是50%透明度的相同按鈕滩字。我們可以注意到,里面的標(biāo)簽的輪廓跟按鈕的背景很不搭調(diào)御吞。
- (void)drawBtn {
self.view.backgroundColor = [UIColor grayColor];
//create opaque button
UIButton *button1 = [self customButton];
button1.center = CGPointMake(100, 150);
[self.view addSubview:button1];
//create translucent button
UIButton *button2 = [self customButton];
button2.center = CGPointMake(300, 150);
button2.alpha = 0.5;
[self.view addSubview:button2];
}
- (UIButton *)customButton {
//create button
CGRect frame = CGRectMake(0, 0, 150, 50);
UIButton *button = [[UIButton alloc] initWithFrame:frame];
button.backgroundColor = [UIColor whiteColor];
button.layer.cornerRadius = 10;
//add label
frame = CGRectMake(20, 10, 110, 30);
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.text = @"Hello World";
label.textAlignment = NSTextAlignmentCenter;
[button addSubview:label];
return button;
}
- 運(yùn)行效果如下
這是由透明度的混合疊加造成的麦箍,當(dāng)你顯示一個(gè)50%透明度的圖層時(shí),圖層的每個(gè)像素都會(huì)一半顯示自己的顏色陶珠,另一半顯示圖層下面的顏色挟裂。這是正常的透明度的表現(xiàn)。但是如果圖層包含一個(gè)同樣顯示50%透明的子圖層時(shí)揍诽,你所看到的視圖诀蓉,50%來(lái)自子視圖,25%來(lái)了圖層本身的顏色暑脆,另外的25%則來(lái)自背景色渠啤。
在我們的示例中,按鈕和表情都是白色背景添吗。雖然他們都是50%的可見度沥曹,但是合起來(lái)的可見度是75%,所以標(biāo)簽所在的區(qū)域看上去就沒有周圍的部分那么透明碟联。所以看上去子視圖就高亮了妓美,使得這個(gè)顯示效果都糟透了。
理想狀況下鲤孵,當(dāng)你設(shè)置了一個(gè)圖層的透明度壶栋,你希望它包含的整個(gè)圖層樹像一個(gè)整體一樣的透明效果。你可以通過(guò)設(shè)置Info.plist
文件中的UIViewGroupOpacity
為YES來(lái)達(dá)到這個(gè)效果普监,但是這個(gè)設(shè)置會(huì)影響到這個(gè)應(yīng)用委刘,整個(gè)app可能會(huì)受到不良影響。如果UIViewGroupOpacity并未設(shè)置鹰椒,iOS 6和以前的版本會(huì)默認(rèn)為NO(也許以后的版本會(huì)有一些改變)。
另一個(gè)方法就是呕童,你可以設(shè)置CALayer的一個(gè)叫做shouldRasterize
屬性來(lái)實(shí)現(xiàn)組透明的效果漆际,如果它被設(shè)置為YES,在應(yīng)用透明度之前夺饲,圖層及其子圖層都會(huì)被整合成一個(gè)整體的圖片奸汇,這樣就沒有透明度混合的問(wèn)題了施符。
為了啟用shouldRasterize
屬性,我們?cè)O(shè)置了圖層的rasterizationScale
屬性擂找。默認(rèn)情況下戳吝,所有圖層拉伸都是1.0, 所以如果你使用了shouldRasterize
屬性贯涎,你就要確保你設(shè)置了rasterizationScale
屬性去匹配屏幕听哭,以防止出現(xiàn)Retina
屏幕像素化的問(wèn)題。
當(dāng)shouldRasterize和UIViewGroupOpacity一起的時(shí)候塘雳,性能問(wèn)題就出現(xiàn)了
button2.layer.shouldRasterize = NO;
button2.layer.rasterizationScale = [UIScreen mainScreen].scale;
- 運(yùn)行結(jié)果如下
七 總結(jié)
這一節(jié)介紹了一些可以通過(guò)代碼應(yīng)用到圖層上的視覺效果陆盘,比如圓角,陰影和蒙板败明。我們也了解了拉伸過(guò)濾器和組透明隘马。
本文摘自 iOS核心動(dòng)畫高級(jí)技巧 - 視覺效果
項(xiàng)目鏈接地址 - AnimationVisualEffect_3