歡迎光臨
每天分享高質量文章

iOS一個方法搞定view漸變色

作者:水暮竹妖

連結:https://www.jianshu.com/p/e7c9e94e165b

Demo

ZCategory

使用效果

//包含頭檔案UIView+Gradient.h

[self.label setGradientBackgroundWithColors:@[[UIColor redColor],[UIColor orangeColor]] locations:nil startPoint:CGPointMake(00) endPoint:CGPointMake(10)];

[self.btn setGradientBackgroundWithColors:@[[UIColor redColor],[UIColor orangeColor]] locations:nil startPoint:CGPointMake(00) endPoint:CGPointMake(10)];

[self.tempView setGradientBackgroundWithColors:@[[UIColor redColor],[UIColor orangeColor]] locations:nil startPoint:CGPointMake(00) endPoint:CGPointMake(10)];


常規方案

說起顏色漸變一般來說有兩種實現方式,一是使用CAGradientLayer而是使用Core Graphics的相關方法,具體可參考ios實現顏色漸變的幾種方法。

問題

CAGradientLayer可以說是實現起來比較方便的一個方案了,但是對於Autolayout來說,我們需要更新CAGradientLayer的frame,這就得增加不少程式碼,而且程式碼會散佈在其他方法中,這就不是很友好了。

最佳化方案

原理

我們知道UIView顯示的內容實際是繪製在CALayer上的,預設情況下建立一個UIView時會建立一個CALayer作為UIView的root layer,那麼如果我們直接把這個root layer替換成CAGradientLayer就能直接實現漸變效果,而且跟隨UIView的frame變化。

那麼如何替換呢?UIView有個+layerClass類方法,官方描述:

Returns the class used to create the layer for instances of this class.


This method returns the CALayer class object by default. Subclasses can override this method and return a different layer class as needed. For example, if your view uses tiling to display a large scrollable area, you might want to override this method and return the CATiledLayer class.


This method is called only once early in the creation of the view in order to create the corresponding layer object.


大概意思就是系統預設會傳回CALayer類,但是如果你重寫了這個類方法,那麼就會返你return的類,從而建立你需要的layer。

在實際實現這個分類的時候發現+layerClass有點類似+load方法,即只要檔案在專案中,就會呼叫該方法,不需要顯式包含頭檔案。

實現

我們建立一個UIView的分類UIView+Gradient,利用runtime新增CAGradientLayer的一些屬性以及設定背景色的方法,如下:

//UIView+Gradient.h
@property(nullablecopyNSArray *colors;
@property(nullablecopyNSArray<NSNumber *> *locations;
@property CGPoint startPoint;
@property CGPoint endPoint;

+ (UIView *_Nullable)gradientViewWithColors:(NSArray<UIColor *> *_Nullable)colors locations:(NSArray<NSNumber *> *_Nullable)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;

- (void)setGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors locations:(NSArray<NSNumber *> *_Nullable)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;

然後在.m檔案中對重寫類方法+layerClass以及實現相關設定:

//UIView+Gradient.m

+ (Class)layerClass {
    return [CAGradientLayer class];
}

+ (UIView *)gradientViewWithColors:(NSArray<UIColor *> *)colors locations:(NSArray<NSNumber *> *)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
    UIView *view = [[self alloc] init];
    [view setGradientBackgroundWithColors:colors locations:locations startPoint:startPoint endPoint:endPoint];
    return view;
}

- (void)setGradientBackgroundWithColors:(NSArray<UIColor *> *)colors locations:(NSArray<NSNumber *> *)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
    NSMutableArray *colorsM = [NSMutableArray array];
    for (UIColor *color in colors) {
        [colorsM addObject:(__bridge id)color.CGColor];
    }
    self.colors = [colorsM copy];
    self.locations = locations;
    self.startPoint = startPoint;
    self.endPoint = endPoint;
}

然後就能像最開始提到的那樣輕鬆的一句話設定漸變背景色了。

註意事項

大部分View在設定colors屬性後原有的backgroundColor屬性會失效,即不管backgroundColor設定的是什麼顏色,都會顯示漸變色,要顯示正常的backgroundColor只需要將colors設定成nil。

// colors優先順序高於backgroundColor的View
    UIView
    UIButton 
    UIImageView
    UITextView
    UITextField
    UISlider
    UIStepper
    UISwitch
    UISegmentedControl

在實測中發現UILabel在設定colors後還是會顯示backgroundColor,要顯示漸變色需要將backgroundColor設定為clearColor。

雖然在UIView的分類中重寫了+layerClass,但是有可能存在一些View 已經重寫了+layerClass,那麼就有可能該View的layer並不是CAGradientLayer,而是其他型別的layer,如UILabel的layer其實是_UILabelLayer,我們在設定layer屬性時不能確保這個layer就是CAGradientLayer,需要加個判斷:

   if ([self.layer isKindOfClass:[CAGradientLayer class]]) {
        // do something
    }

否則可能會出現崩潰。

對於UILabel這種貌似已經重寫過+layerClass方法的view,我目前是直接在其分類中再次重寫+layerClass方法

//  UIView+Gradient.m
    @implementation UILabel (Gradient)
    + (Class)layerClass {
     return [CAGradientLayer class];
    }
    @end

將_UILabelLayer替換為CAGradientLayer除了colors和backgroundColor的優先順序不同其他還有什麼影響暫時不清楚。


編號280,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

Web開發

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂