preface
You know UIView supports AutoLayout, CALayer doesn’t. This is because UIView is responsible for event response, layout information storage, and so on, while CALayer is responsible for page content presentation. The relationship between UIView and CALayer is a bit like “I am responsible for making money to support my family, and you are responsible for being beautiful”. For a more complete comparison, see the difference and connection between CALayer and UIView. However, sometimes when you use a CALayer, you need to depend on the layout of other views, depending on the superview. As you can see below, dynamically adjust the gradient background CAShapeLayer placed under the UILabel based on the length of the text. So how do we do that?
Implementation scheme
The traditional method
The usual way to do this is in the viewDidLayoutSubviews aspect, adjust the CALayer frame according to the dependent view.
- (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 needs margins around it
CGRect frame = self.testLabel.frame;
frame.origin.x -= margin;
frame.size.width += margin*2;
self.gradientLayer.frame = frame;
}
Copy the code
Disadvantages of the scheme:
- In OC, because the control’s scope has crossed methods, you need a property to reference it.
- The code is scattered and difficult to manage.
A new scheme
Each UIView has a CALayer at the bottom, and UIView has an API+ (Class)layerClass that allows us to customize the classes of the CALayer. Then we can customize a UILabel and override the + (Class)layerClass method to return the CAGradientLayer. We just constrain UILabel, and it maps to its own 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
Copy the code
- Advantages & Disadvantages Advantages: As long as you package UILabel and have similar needs, you can use it directly. Disadvantages: because you need to customize a class before using it, it is impossible to subclass all similar controls. Sometimes it’s just a small requirement that has a similar requirement for a small control. Then the second option can be adopted.
The new scheme
Call UIView’s layoutSubviews with block callbacks, so you can flexibly layout the CALayer. First, create a new UIView category:
@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
Copy the code
External use:
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;
};
Copy the code
So that takes care of the problem of code fragmentation and custom classes. Download the source code from 👏 and drag UIView+ LayoutSubViewScallback. h to use it
Code warehouse
If you like Github address, click ❤️