優雅的實現CALayer的"AutoLayout"的兩種方案

前言

你們都知道UIView支持AutoLayout,而CALayer不能夠。這是由於UIView負責事件響應、佈局信息存儲等,而CALayer主要負責頁面內容展現。UIView和CALayer的關係,很有一點**「我負責賺錢養家,你負責貌美如花」**的感受。關於它們更全面的比較,能夠參考詳解CALayer 和 UIView的區別和聯繫 可是,有時候使用CALayer的時候,須要根據父視圖、獲取依賴其它視圖佈局。好比下圖所示,根據UILabel文本的長度,動態調整放在它下面的漸變背景CAShapeLayer。那咱們該如何實現呢? git

JXLayerAutoLayout-1.png

實現方案

傳統方法

一般的作法是在viewDidLayoutSubviews的方面裏面,根據依賴的視圖來調整CALayer的frame。github

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self.testLabel sizeToFit];
    self.testLabel.center = self.view.center;

    self.gradientLayer.cornerRadius = self.testLabel.frame.size.height/2;
    CGFloat margin = 10;//gradientLayer左右須要有邊距
    CGRect frame = self.testLabel.frame;
    frame.origin.x -= margin;
    frame.size.width += margin*2;
    self.gradientLayer.frame = frame;
}
複製代碼

該方案的缺點:swift

  • 在OC裏面由於控件的使用範圍已經跨方法,須要用一個屬性來引用它。
  • 代碼分散,很差管理。

新方案一

每一個UIView底層都有一個CALayer,而且UIView有一個API+ (Class)layerClass,容許咱們自定義CALayer的類。那咱們就能夠自定義一個UILabel,重寫+ (Class)layerClass方法返回CAGradientLayer。咱們只須要對UILabel進行約束,就會映射到它自身的CALayer。佈局

@implementation CustomLabel

+ (Class)layerClass {
    return [CAGradientLayer class]; } - (void)layoutSubviews {
    [super layoutSubviews];

    CAGradientLayer *gradientLayer = (CAGradientLayer *)self.layer;
    gradientLayer.colors = @[(id)[[UIColor alloc] initWithRed:16/255.0 green:175/255.0 blue:211/255.0 alpha:1].CGColor, (id)[[UIColor alloc] initWithRed:33/255.0 green:94/255.0 blue:147/255.0 alpha:1].CGColor];
    gradientLayer.locations = @[@(0), @(1)];
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(1, 0);

    self.layer.cornerRadius = self.bounds.size.height/2.0;
}

@end
複製代碼
  • 優勢&缺點 優勢:只要封裝好UILabel,有相似的需求,直接拿來用就是了。 缺點:就是由於使用前要自定義一個類,不可能對全部相似要求的控件子類化。有時候只是一個小需求,對一個小控件有相似需求。那麼就能夠採用第二個方案。

新方案二

經過block回調UIView的layoutSubviews,這樣就能夠很靈活的佈局CALayer了。 首先新建一個UIView的分類:spa

@implementation UIView (LayoutSubviewsCallback)

+ (void)load {
    Method originalMethod = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method newMethod = class_getInstanceMethod(self, @selector(jx_layoutSubviews));
    method_exchangeImplementations(originalMethod, newMethod);
}

- (void)jx_layoutSubviews {
    [self jx_layoutSubviews];

    !self.layoutSubviewsCallback ?: self.layoutSubviewsCallback(self);
}

- (void)setLayoutSubviewsCallback:(void (^)(UIView *))layoutSubviewsCallback {
    objc_setAssociatedObject(self, "layoutSubviewsCallback", layoutSubviewsCallback, OBJC_ASSOCIATION_RETAIN);
}

- (void (^)(UIView *))layoutSubviewsCallback {
    return objc_getAssociatedObject(self, "layoutSubviewsCallback");
}
@end
複製代碼

外部使用:3d

CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.colors = @[(id)[[UIColor alloc] initWithRed:16/255.0 green:175/255.0 blue:211/255.0 alpha:1].CGColor, (id)[[UIColor alloc] initWithRed:33/255.0 green:94/255.0 blue:147/255.0 alpha:1].CGColor];
    gradientLayer.locations = @[@(0), @(1)];
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(1, 0);
    [self.view.layer addSublayer:gradientLayer];
    self.testLabel.layoutSubviewsCallback = ^(UIView *view) {
        gradientLayer.frame = view.frame;
        gradientLayer.cornerRadius = view.bounds.size.height/2.0;
    };
複製代碼

這樣就解決了,代碼分散,自定義類的麻煩了。想在哪裏用,就在哪裏用👏 下載源碼,拖入UIView+LayoutSubviewsCallback.h文件,就可使用了code

代碼倉庫

Github地址喜歡就點顆❤️cdn

相關文章
相關標籤/搜索