图片圆角优化工具的概述
该库是通过UIImageView写一个分类,调用一行代码实现圆角的优化:imageview.aliCornerRadius = 5.0f
,核心思想就是使用圆角图片替换系统圆角
核心技术点
- KVO观察者模式的使用
@interface HJImageObserver : NSObject
@property (nonatomic, assign) UIImageView *originImageView;
@property (nonatomic, strong) UIImage *originImage;
@property (nonatomic, assign) CGFloat cornerRadius;
- (instancetype)initWithImageView:(UIImageView *)imageView;
@end
@implementation HJImageObserver
- (void)dealloc {
[self.originImageView removeObserver:self forKeyPath:@"image"];
[self.originImageView removeObserver:self forKeyPath:@"contentMode"];
}
- (instancetype)initWithImageView:(UIImageView *)imageView{
if (self = [super init]) {
self.originImageView = imageView;
[imageView addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:nil];
[imageView addObserver:self forKeyPath:@"contentMode" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString*, id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"image"]) {
UIImage *newImage = change[@"new"];
if (![newImage isKindOfClass:[UIImage class]] || newImage.aliCornerRadius) {
return;
}
[self updateImageView];
}
if ([keyPath isEqualToString:@"contentMode"]) {
self.originImageView.image = self.originImage;
}
}
通过创建动态属性观察者HJImageObserver
,监听image
和contentMode
属性,当image变化时,能够触发圆角图片的生成,从而达到自动实现圆角图片替换的目的
- 截图渲染代码
- (void)updateImageView {
self.originImage = self.originImageView.image;
if (!self.originImage) {
return;
}
UIImage *image = nil;
UIGraphicsBeginImageContextWithOptions(self.originImageView.bounds.size, NO, [UIScreen mainScreen].scale);
CGContextRef currnetContext = UIGraphicsGetCurrentContext();
if (currnetContext) {
CGContextAddPath(currnetContext, [UIBezierPath bezierPathWithRoundedRect:self.originImageView.bounds cornerRadius:self.cornerRadius].CGPath);
CGContextClip(currnetContext);
[self.originImageView.layer renderInContext:currnetContext];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
if ([image isKindOfClass:[UIImage class]]) {
image.aliCornerRadius = YES;
self.originImageView.image = image;
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateImageView];
});
}
}
这段代码不是直接渲染原始Image对象,而是先截取UIImageView视图Layer生成的Image,然后再做渲染。原因很简单,因为UIImageView呈现方式涉及多种ContentMode,通过渲染UIImageView视图Layer生成的图片可以巧妙的解决UIImageView显示模式的问题。
此外,由于系统原因,对于像UITableViewCell的UIImageView,第一次创建赋图时,可能无法获取UIImageView视图Layer的图片,此时,可以通过切换异步RunLoop达到延时渲染的目的
- 分类添加属性代码巧用
@interface UIImage (cornerRadius)
@property (nonatomic, assign) BOOL aliCornerRadius;
@end
@implementation UIImage (cornerRadius)
- (BOOL)aliCornerRadius {
return [objc_getAssociatedObject(self, @selector(aliCornerRadius)) boolValue];
}
- (void)setAliCornerRadius:(BOOL)aliCornerRadius {
objc_setAssociatedObject(self, @selector(aliCornerRadius), @(aliCornerRadius), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end
在setAliCornerRadius:方法中使用了一个objc_setAssociatedObject
的方法,这个方法有四个参数,分别是:源对象,关联时用来标记是哪一个属性的key(因为你可能要添加很多属性),关联的对象和一个关联策略,这里我们可以看出一大亮点,就是关联的key值所用的写法,一般属性的key我们会这样写:
//利用静态变量地址唯一不变的特性
1、static void *strKey = &strKey;
2、static NSString *strKey = @"strKey";
3、static char strKey;
1)但是这段代码里的key
将Selector
作为key
。在我们需要创建出许多的分类属性时,以上述的方式定义出许多的static const keys无疑是比较繁琐的事情,还好有个方法可以避免繁琐事件:
我们为关联对象所使用的参数key
是一个类型static const void *
的指针。Objective-C
中的selector
其实质上也是一个常量指针,这意味着它们很适合作为关联对象的key
值。假如你用关联对象的方式去实现一个属性,你可以使用这个属性的getter
方法的方法名作为这个key
。
2) 这里我们还可以使用_cmd
简化下代码,_cmd
在Objective-C
的方法中表示当前方法的selector
,所以我们简化下get
方法的写法:
- (BOOL)aliCornerRadius{
return [objc_getAssociatedObject(self, _cmd) boolValue];
}