问题
我们在 coding 的征途中,总会写下几个(or 几十个?)xxxUtils
之类的东西。想象有这样一个 NVMImageUtils
,一开始它的接口是这样的:
+ (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)UIColor { }
|
马上需要加个参数 borderColor,是可选的,于是接口成了这样:
+ (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)UIColor { return [self imageWithSize:size color:color borderColor:nil]; } + (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)UIColor borderColor:(UIColor *)borderColor { }
|
过段时间再加个参数 cornerRadius:
+ (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)UIColor { return [self imageWithSize:size color:color borderColor:nil]; } + (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)UIColor borderColor:(UIColor *)borderColor { return [self imageWithSize:size color:color borderColor:borderColor cornerRadius:nil]; } + (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)UIColor cornerRadius:(NSNumber *) cornerRadius { return [self imageWithSize:size color:color borderColor:nil cornerRadius:cornerRadius]; } + (UIImage *)imageWithSize:(CGSize)size color:(UIColor *)UIColor borderColor:(UIColor *)borderColor cornerRadius:(NSNumber *)cornerRadius { }
|
要是再加一个 borderWidth,估计就 hold 不住了。。。
解决方案
通过玩弄 objc 的语法,最终的效果如下,接口可被链式调用,且需要什么可选参数就调什么:
UIImage *image = nvm_beginImage(size) .fillColor(fillColor) .borderColor(borderColor) .cornerRadius(20) .opacity(0.5) .make;
|
原理
头文件如下,声明一个类叫 NVMImageMaker
,用来储存 make 一个 image 需要的各种参数,最终可以通过这些参数 make 出 image:
@interface NVMImageMaker : NSObject - (NVMImageMaker* (^)(UIColor* imageFillColor))fillColor; - (NVMImageMaker* (^)(UIColor* imageBorderColor))borderColor; - (NVMImageMaker* (^)(CGFloat imageBorderWidth))borderWidth; - (NVMImageMaker* (^)(CGFloat imageCornerRadius))cornerRadius; - (NVMImageMaker* (^)(CGFloat opacity))opacity; - (UIImage *)make; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @end extern NVMImageMaker* nvm_beginImage(CGSize imageSize);
|
接口的入口是 nvm_beginImage()
,接收的是必须的参数,这里是 imageSize
,用来返回一个 NVMImageMaker
对象,
extern NVMImageMaker *_Nonnull nvm_beginImage(CGSize imageSize) { NVMImageMaker *maker = [NVMImageMaker new]; maker.imageSize = imageSize; return maker; }
|
以 fillColor
为例,看一看如何实现链式调用传入可选参数 fillColor
:
- (NVMImageMaker * (^)(UIColor * imageFillColor))fillColor { return ^NVMImageMaker *(UIColor *imageFillColor) { self.imageFillColor = imageFillColor; return self; }; }
|
这里 fillColor
实际返回的是一个 block,这个 block 接收一个真正的 imageFillColor
,储存到 self (NVMImageMaker
),然后将 self 返回。配合用 dot 语法调用方法的特性,我们可以做到:
nvm_beginImage(size).fillColor(color) | ① | ② | ③ |
|
依此类推,我们可以用这样的思路添加任意的可选参数。
最后,make
方法综合所有得到的参数,绘制成图片返回(实现忽略,不是本篇重点),最终得以通过链式调用得到图片:
nvm_beginImage(size).fillColor(color).make;
|
总结
代码地址:https://github.com/eleme/NVMImageMaker。可以直接通过 pod 使用,当然实现并不复杂,大可自行实现。
至此,我们有了一个全新的思路,用来解决开篇演示的,让人越来越 hold 不住的多可选参数接口的设计问题。
Written by 饿了么iOS组 - axl411