1.发现问题
假如我们写一个UILabel,我们的代码会可能会如下所示
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
label.text = @"Hello world!";
label.textColor = [UIColor whiteColor];
label.font = [UIFont systemFontOfSize:15];
label.textAlignment = NSTextAlignmentCenter;
label.backgroundColor = [UIColor redColor];
label.layer.cornerRadius = 25;
label.clipsToBounds = YES;
[self.view addSubview:label];
这段代码有什么问题呢? 没错!就是非常臃肿,像这些简单的模块如果不加以封装,项目中代码量将会非常多,既不利于阅读,也不利于维护.
2.初步封装
在分类中添加方法
// .h
+ (UILabel *)labelWithText:(NSString *)text textColor:(UIColor *)color font:(UIFont *)font textAlign:(NSTextAlignment)align bgColor:(UIColor *)bgColor cornerRadius:(CGFloat)radius .......;
// .m
+ (UILabel *)labelWithText:(NSString *)text textColor:(UIColor *)color font:(UIFont *)font textAlign:(NSTextAlignment)align bgColor:(UIColor *)bgColor cornerRadius:(CGFloat)radius ....... {
UILabel *label = [[UILabel alloc] init];
label.text = text;
label.textColor = color;
...
return label;
}
这样的话,我们每次在初始化一个控件的时候,只需要1行代码就可以基本完成问题.但是这样写也有一个新的问题,就是可拓展性的问题.
-----假如我们只需要设置text和textColor,我们需要写上这么一行,然后后边参数传空
-----假如我们需要另外设置numOfLines或者其他没有封装到的属性,我们也需要写上这么一行,然后额外设置其他属性
3.继续优化
最近再看Masonry源码,其中的链式编程思想对我有很大的启发,我想是否可以采用block的形式设置属性,并用链式编程将他它们链接起来呢?
说干就干!
.h文件
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef UILabel* (^xl_Block)(id);
@interface UILabel (X)
@property (nonatomic, copy, readonly) xl_Block x_text;
@property (nonatomic, copy, readonly) xl_Block x_font;
@property (nonatomic, copy, readonly) xl_Block x_textColor;
@property (nonatomic, copy, readonly) xl_Block x_align;
@property (nonatomic, copy, readonly) xl_Block x_numOfLines;
@property (nonatomic, copy, readonly) xl_Block x_shadowOffset;
@property (nonatomic, copy, readonly) xl_Block x_lineBreakMode;
@property (nonatomic, copy, readonly) xl_Block x_attributedText;
@end
NS_ASSUME_NONNULL_END
.m文件
#import "UILabel+X.h"
#import "XKit.h"
@implementation UILabel (X)
- (xl_Block)x_text
{
xl_Block block = ^UILabel* (NSString *text) {
self.text = text;
return self;
};
return block;
}
- (xl_Block)x_font {
xl_Block block = ^UILabel* (UIFont *font) {
self.font = font;
return self;
};
return block;
}
- (xl_Block)x_textColor {
xl_Block block = ^UILabel* (UIColor *color) {
self.textColor = color;
return self;
};
return block;
}
- (xl_Block)x_align {
xl_Block block = ^UILabel* (NSNumber *align) {
self.textAlignment = align.integerValue;
return self;
};
return block;
}
- (xl_Block)x_numOfLines {
xl_Block block = ^UILabel* (NSNumber *num) {
self.numberOfLines = num.integerValue;
return self;
};
return block;
}
- (xl_Block)x_shadowOffset {
xl_Block block = ^UILabel *(NSValue *value) {
self.shadowOffset = value.CGSizeValue;
return self;
};
return block;
}
- (xl_Block)x_lineBreakMode {
xl_Block block = ^UILabel *(NSNumber *mode) {
self.lineBreakMode = mode.integerValue;
return self;
};
return block;
}
- (xl_Block)x_attributedText {
xl_Block block = ^UILabel *(NSAttributedString *attr) {
self.attributedText = attr;
return self;
};
return block;
}
@end
这样,我们的代码便可以这样写
UILabel *label = [UILabel new];
label
.x_text(@"Hello world!")
.x_textColor([UIColor whiteColor])
.x_font([UIFont systemFontOfSize:15])
.x_align(@(NSTextAlignmentCenter))
.x_sizeToFit(@(YES));
label
.x_cornerRadius(@(15))
.x_clipToBounds(@(YES))
.x_backgroundColor([UIColor redColor]);
[self.view addSubview:label];
是不是很神奇😆
4.项目地址
具体源码可以去我的github上下载
前往github
希望大家多提意见和建议,可以的话给个小星星奖励
5.存在的问题
1.以UILabel 和 UIView为例
写分类方法的时候,我将
UILabel的属性写到UILabel+X中,其中block返回的为UILabel对象
UIView的属性写到UIView+X中,其中的block返回的为UIView对象
所以就会出现例如label.x_backgroundColor([UIColor whiteColor]) 之后再去调用UILabel的属性后报错的情况
目前暂时的解决方法:写的时候要先写UILabel的属性,再写UIView的属性,例如:
UILabel *label = [UILabel new];
label
.x_text(@"Hello world!")
.x_textColor([UIColor whiteColor])
.x_font([UIFont systemFontOfSize:15])
.x_align(@(NSTextAlignmentCenter))
.x_sizeToFit(@(YES));
// 上边为UILabel的属性,下边为父类UIView的属性
.x_cornerRadius(@(15))
.x_clipToBounds(@(YES))
.x_backgroundColor([UIColor redColor]);
[self.view addSubview:label];
2.以UITableView为例,如果想使用链式及block的形式实现UITableView的代理方法,便会自动设置delegate 和 datasource为当前的tableview,并走UITableView+X中实现的delegate 和 datasource方法,之前设置的delegate 和 dataSource 将会无效,例如:
UITableView *table = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
table
.x_rowHeight(@60)
.x_sectionHeaderHeight(@0.01) // 在iOS11之后需要设置该属性,才会走heightForHeader的代理方法
.x_sectionFooterHeight(@0.01) // 在iOS11之后需要设置该属性,才会走heightForFooter的代理方法
.x_registerCell([UITableViewCell class], cellID)
.x_delegate(self)
.x_dataSource(self)
// 如果实现了delegate 或者 datasource的block,就会自动设置当前为delegate和datasource为table本身,并走UITableView+X中实现的delegate 和 datasource方法,之前设置的delegate 和 dataSource 将会无效
.x_viewForHeaderInSection(^UIView * _Nonnull(UITableView * _Nonnull table, NSInteger section) {
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, 200)];
header.backgroundColor = [UIColor redColor];
return header;
})
.x_viewForFooterInSection(^UIView * _Nonnull(UITableView * _Nonnull table, NSInteger section) {
UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, 200)];
footer.backgroundColor = [UIColor blueColor];
return footer;
})
.x_heightForFooterInSection(^NSInteger(UITableView * _Nonnull table, NSInteger section) {
return 20;
})
.x_heightForHeaderInSection(^NSInteger(UITableView * _Nonnull table, NSInteger section) {
return 20;
})
.x_numofSectionsInTableView(^NSInteger(UITableView * _Nonnull table) {
return self.dataSource.count;
})
.x_numOfRowsInSection(^NSInteger(UITableView * _Nonnull table, NSInteger section) {
return [self.dataSource[section] count];
})
.x_cellForRowAtIndexPath(^UITableViewCell * _Nonnull(UITableView * _Nonnull table, NSIndexPath * _Nonnull indexpath) {
UITableViewCell *cell = [table dequeueReusableCellWithIdentifier:cellID forIndexPath:indexpath];
cell.textLabel.text = self.dataSource[indexpath.section][indexpath.row];
return cell;
})
;
[self.view addSubview:table];
3.后续我们持续更新该框架,感谢大家支持!
网友评论