[iOS Animation]-CALayer 視覺效果-拉伸過濾

拉伸過濾

最後咱們再來談談minificationFilter和magnificationFilter屬性。總得來說,當咱們視圖顯示一個圖片的時候,都應該正確地顯示這個圖片(意即:以正確的比例和正確的1:1像素顯示在屏幕上)。緣由以下: git

  • 可以顯示最好的畫質,像素既沒有被壓縮也沒有被拉伸。
  • 能更好的使用內存,由於這就是全部你要存儲的東西。
  • 最好的性能表現,CPU不須要爲此額外的計算。

不過有時候,顯示一個非真實大小的圖片確實是咱們須要的效果。好比說一個頭像或是圖片的縮略圖,再好比說一個能夠被拖拽和伸縮的大圖。這些狀況下,爲同一圖片的不一樣大小存儲不一樣的圖片顯得又不切實際。 github

當圖片須要顯示不一樣的大小的時候,有一種叫作拉伸過濾的算法就起到做用了。它做用於原圖的像素上並根據須要生成新的像素顯示在屏幕上。 算法

事實上,重繪圖片大小也沒有一個統一的通用算法。這取決於須要拉伸的內容,放大或是縮小的需求等這些因素。CALayer爲此提供了三種拉伸過濾方法,他們是: 數組

  • kCAFilterLinear
  • kCAFilterNearest
  • kCAFilterTrilinear

minification(縮小圖片)和magnification(放大圖片)默認的過濾器都是kCAFilterLinear,這個過濾器採用雙線性濾波算法,它在大多數狀況下都表現良好。雙線性濾波算法經過對多個像素取樣最終生成新的值,獲得一個平滑的表現不錯的拉伸。可是當放大倍數比較大的時候圖片就模糊不清了。 app

kCAFilterTrilinear和kCAFilterLinear很是類似,大部分狀況下兩者都看不出來有什麼差異。可是,較雙線性濾波算法而言,三線性濾波算法存儲了多個大小狀況下的圖片(也叫多重貼圖),並三維取樣,同時結合大圖和小圖的存儲進而獲得最後的結果。 性能

這個方法的好處在於算法可以從一系列已經接近於最終大小的圖片中獲得想要的結果,也就是說不要對不少像素同步取樣。這不只提升了性能,也避免了小几率因舍入錯誤引發的取樣失靈的問題 字體

圖4.14

圖4.14 對於大圖來講,雙線性濾波和三線性濾波表現得更出色 ui

kCAFilterNearest是一種比較武斷的方法。從名字不難看出,這個算法(也叫最近過濾)就是取樣最近的單像素點而無論其餘的顏色。這樣作很是快,也不會使圖片模糊。可是,最明顯的效果就是,會使得壓縮圖片更糟,圖片放大以後也顯得塊狀或是馬賽克嚴重。 atom

圖4.15

圖4.15 對於沒有斜線的小圖來講,最近過濾算法要好不少 spa

總的來講,對於比較小的圖或者是差別特別明顯,極少斜線的大圖,最近過濾算法會保留這種差別明顯的特質以呈現更好的結果。可是對於大多數的圖尤爲是有不少斜線或是曲線輪廓的圖片來講,最近過濾算法會致使更差的結果。換句話說,線性過濾保留了形狀,最近過濾則保留了像素的差別。

讓咱們來實驗一下。咱們對第三章的時鐘項目改動一下,用LCD風格的數字方式顯示。咱們用簡單的像素字體(一種用像素構成字符的字體,而非矢量圖形)創造數字顯示方式,用圖片存儲起來,並且用第二章介紹過的拼合技術來顯示(如圖4.16)。

圖4.16

圖4.16 一個簡單的運用拼合技術顯示的LCD數字風格的像素字體

咱們在Interface Builder中放置了六個視圖,小時、分鐘、秒鐘各兩個,圖4.17顯示了這六個視圖是如何在Interface Builder中放置的。若是每一個都用一個淡出的outlets對象就會顯得太多了,因此咱們就用了一個IBOutletCollection對象把他們和控制器聯繫起來,這樣咱們就能夠以數組的方式訪問視圖了。清單4.6是代碼實現。

清單4.6 顯示一個LCD風格的時鐘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@interface ViewController ()
 
@property (nonatomic, strong) IBOutletCollection(UIView) NSArray *digitViews;
@property (nonatomic, weak) NSTimer *timer;

@end
 
@implementation ViewController
 
- (void)viewDidLoad
{
  [super viewDidLoad]; //get spritesheet image
  UIImage *digits = [UIImage imageNamed:@"Digits.png"];
 
  //set up digit views
  for (UIView *view in self.digitViews) {
    //set contents
    view.layer.contents = (__bridge id)digits.CGImage;
    view.layer.contentsRect = CGRectMake(0, 0, 0.1, 1.0);
    view.layer.contentsGravity = kCAGravityResizeAspect;
  }
 
  //start timer
  self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];
 
  //set initial clock time
  [self tick];
}
 
- (void)setDigit:(NSInteger)digit forView:(UIView *)view
{
  //adjust contentsRect to select correct digit
  view.layer.contentsRect = CGRectMake(digit * 0.1, 0, 0.1, 1.0);
}
 
- (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]];
}
@end

如圖4.18,這樣作的確起了效果,可是圖片看起來模糊了。看起來默認的kCAFilterLinear選項讓咱們失望了。

圖4.18

圖4.18 一個模糊的時鐘,由默認的kCAFilterLinear引發

爲了能像圖4.19中那樣,咱們須要在for循環中加入以下代碼:

1
view.layer.magnificationFilter = kCAFilterNearest;

圖4.19

圖4.19 設置了最近過濾以後的清晰顯示

組透明

UIView有一個叫作alpha的屬性來肯定視圖的透明度。CALayer有一個等同的屬性叫作opacity,這兩個屬性都是影響子層級的。也就是說,若是你給一個圖層設置了opacity屬性,那它的子圖層都會受此影響。

iOS常見的作法是把一個空間的alpha值設置爲0.5(50%)以使其看上去呈現爲不可用狀態。對於獨立的視圖來講還不錯,可是當一個控件有子視圖的時候就有點奇怪了,圖4.20展現了一個內嵌了UILabel的自定義UIButton;左邊是一個不透明的按鈕,右邊是50%透明度的相同按鈕。咱們能夠注意到,裏面的標籤的輪廓跟按鈕的背景很不搭調。

圖4.20

圖4.20 右邊的漸隱按鈕中,裏面的標籤清晰可見

這是由透明度的混合疊加形成的,當你顯示一個50%透明度的圖層時,圖層的每一個像素都會通常顯示本身的顏色,另外一半顯示圖層下面的顏色。這是正常的透明度的表現。可是若是圖層包含一個一樣顯示50%透明的子圖層時,你所看到的視圖,50%來自子視圖,25%來了圖層自己的顏色,另外的25%則來自背景色。

在咱們的示例中,按鈕和表情都是白色背景。雖然他們都是50%的可見度,可是合起來的可見度是75%,因此標籤所在的區域看上去就沒有周圍的部分那麼透明。因此看上去子視圖就高亮了,使得這個顯示效果都糟透了。

理想情況下,當你設置了一個圖層的透明度,你但願它包含的整個圖層樹像一個總體同樣的透明效果。你能夠經過設置Info.plist文件中的UIViewGroupOpacity爲YES來達到這個效果,可是這個設置會影響到這個應用,整個app可能會受到不良影響。若是UIViewGroupOpacity並未設置,iOS 6和之前的版本會默認爲NO(也許之後的版本會有一些改變)。

另外一個方法就是,你能夠設置CALayer的一個叫作shouldRasterize屬性(見清單4.7)來實現組透明的效果,若是它被設置爲YES,在應用透明度以前,圖層及其子圖層都會被整合成一個總體的圖片,這樣就沒有透明度混合的問題了(如圖4.21)。

爲了啓用shouldRasterize屬性,咱們設置了圖層的rasterizationScale屬性。默認狀況下,全部圖層拉伸都是1.0, 因此若是你使用了shouldRasterize屬性,你就要確保你設置了rasterizationScale屬性去匹配屏幕,以防止出現Retina屏幕像素化的問題。

當shouldRasterize和UIViewGroupOpacity一塊兒的時候,性能問題就出現了(咱們在第12章『速度』和第15章『圖層性能』將作出介紹),可是性能碰撞都本地化了(譯者注:這句話須要再翻譯)。

清單4.7 使用shouldRasterize屬性解決組透明問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@end
 
@implementation ViewController
 
- (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;
}
 
- (void)viewDidLoad
{
  [super viewDidLoad];
 
  //create opaque button
  UIButton *button1 = [self customButton];
  button1.center = CGPointMake(50, 150);
  [self.containerView addSubview:button1];
 
  //create translucent button
  UIButton *button2 = [self customButton];
  
  button2.center = CGPointMake(250, 150);
  button2.alpha = 0.5;
  [self.containerView addSubview:button2];
 
  //enable rasterization for the translucent button
  button2.layer.shouldRasterize = YES;
  button2.layer.rasterizationScale = [UIScreen mainScreen].scale;
}
@end

圖4.12

圖4.21 修正後的圖

總結

這一章介紹了一些能夠經過代碼應用到圖層上的視覺效果,好比圓角,陰影和蒙板。咱們也瞭解了拉伸過濾器和組透明。

相關文章
相關標籤/搜索