iOS APP开发过程中,无论MVC、MVP、MVVM、VIPER等何种架构模式,都会有控件模块的一席之地。因为客户端嘛,使用起来主要与客户打交道的还是控件,而从代码数量上来看,控件部分占据的比例也是非常大的,无论是构造,设置,展示等等,都充斥在项目中的各个角落。所以针对控件的管理就显得至关重要。
一般来讲,绝大部分情况下,控件的管理部分属于业务端(以下称为biz)范畴。今天就来谈一谈,如何在biz层面去做控件统一管理与派发。
引出两个名词:(名字自己起的,如果觉得不合适可以自行理解,不必纠结此处)
1.创建工厂:管理控件的构造入口,建议一切可复用控件创建入口都由此类来派发
2.样式工厂:管理各类控件的特殊样式化
创建工厂(createFactory)
上面已经说过,主要用来管理各种可重用控件的构造创建。例如:app中最常用的就是table与cell的组合了,没有之一。各个自定义的cell,无论是纯code还是xib,都是要散落在各个地方的,即使类文件在层级分明,命名明确的文件夹中,也难免对于后加入到项目中的人,造成寻找困难,甚至很大程度上,都找不到,工程与团队规模越大,这个情况越明显。结果就是相同的cell,你写一个,我写一个,他写一个。这是我们在纵观项目结构时,不希望看到的情况,也是造成代码冗余的一部分原因。此时创建工厂就派上用场了:
@interface SNViewCreateFactory :NSObject
+ (UITableViewCell *)cellWithStyle:(SNCellStyle)style forTableView:(UITableView *)tableView;
+ (UIButton *)buttonWithStyle:(SNButtonStyle)style;
+ (UIView *)viewWithStyle:(SNViewStyle)style;
@end
实现:
@implementation SNViewCreateFactory
+ (UITableViewCell *)cellWithStyle:(SNCellStyle)style forTableView:(UITableView *)tableView
{
switch (style) {
case SNCellStyleSystemDefault:
return [tableView systemDefaultCell];
break;
case SNCellStyleSystemValue1:
return [tableView systemValue1Cell];
break;
case SNCellStyleSystemSubTitle:
return [tableView systemSubTitleCell];
break;
default:
return [UITableViewCell new];
break;
}
}
@end
typedef NS_ENUM(NSUInteger, SNCellStyle) {
//系统样式
SNCellStyleSystemDefault, //系统样式 默认
SNCellStyleSystemValue1, //系统样式 右侧带有detailLabel
SNCellStyleSystemSubTitle, //系统样式 主标题下方有detailLabel
//通用样式
//业务样式
};
@interface UITableView (SNCellStyle)
#pragma mark - 系统样式
- (UITableViewCell *)systemDefaultCell;
- (UITableViewCell *)systemValue1Cell;
- (UITableViewCell *)systemSubTitleCell;
#pragma mark - 通用样式
#pragma mark - 业务样式
@end
实现:
@implementation UITableView (SNCellStyle)
#pragma mark - 系统样式
- (UITableViewCell *)systemDefaultCell
{
static NSString *cellIdentifier = @"systemDefaultCell";
UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
}
return cell;
}
- (UITableViewCell *)systemValue1Cell
{
static NSString *cellIdentifier = @"systemValue1Cell";
UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:cellIdentifier];
}
return cell;
}
- (UITableViewCell *)systemSubTitleCell
{
static NSString *cellIdentifier = @"systemSubTitleCell";
UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
return cell;
}
#pragma mark - 通用样式
#pragma mark - 业务样式
@end
这里只是拿几个系统样式的cell为例,其余请自行举一反三。
样式工厂(styleFactory)
样式工厂,主要解决的是管理散落在各个角落中针对某些共性样式设置的代码。此工厂个人认为非常实用,并且项目规模越庞大,就越为实用。UED在设计过程中,绝大多数一定会将一个APP的各个角落设计成统一风格的(不排除有小部分奇葩设计),即使中途更换过设计人员,也一定是尽可能往直前的风格上靠,又或者更改之前的,全部推翻重新换套皮肤。总之,一个正常的APP,内部各个页面,各个相同类型的控件,几乎是完全一样的设置,而统一管理这些设置,就是这个样式工厂做的事情了:
@interface SNViewStyleFactory : NSObject
/**
按钮样式设置
@param style 样式类型
@param button 被设置的按钮
*/
+ (void)buttonWithStyle:(SNButtonSettingStyle)style targetButton:(UIButton *)button;
/**
视图样式设置
@param style 样式类型
@param view 被设置的视图
*/
+ (void)viewWithStyle:(SNViewSettingStyle)style targetView:(UIView *)view;
@end
实现:(实在是懒得把demo写全,意会一下吧)
@implementation SNViewStyleFactory
+ (void)buttonWithStyle:(SNButtonSettingStyle)style targetButton:(UIButton *)button
{
switch (style) {
case SNButtonSettingStyleBottomBlue:
[button bottomBlue];
break;
case SNButtonSettingStyleBorderRounded:
[button borderRounded];
break;
case SNButtonSettingStyleBottomSuspendBlue:
[button bottomSuspendBlue];
break;
default:
break;
}
}
@end
控件这边,建议写成类别
typedef enum : NSUInteger {
SNButtonSettingStyleBottomBlue, //底部蓝色按钮
SNButtonSettingStyleBottomSuspendBlue, //底部蓝色悬浮按钮
SNButtonSettingStyleBorderRounded, //圆角边框按钮
} SNButtonSettingStyle;
@interface UIButton (SNStyle)
//底部蓝色按钮
- (void)bottomBlue;
//底部蓝色悬浮按钮
- (void)bottomSuspendBlue;
//圆角边框按钮
- (void)borderRounded;
@end
实现:
@implementation UIButton (SNStyle)
- (void)bottomBlue
{
self.backgroundColor = [UIColor blueColor];
self.layer.cornerRadius = 4;
self.layer.masksToBounds = YES;
self.titleLabel.font = [UIFont systemFontOfSize:18];
}
- (void)bottomSuspendBlue
{
self.backgroundColor = [UIColor blueColor];
self.titleLabel.font = [UIFont systemFontOfSize:18];
}
- (void)borderRounded
{
self.backgroundColor = [UIColor blueColor];
self.titleLabel.font = [UIFont systemFontOfSize:14];
self.layer.cornerRadius = 4;
self.layer.masksToBounds = YES;
self.layer.borderWidth = 0.5;
self.layer.borderColor = [[UIColor redColor] CGColor];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
@end
调用
如果对以上描述没有一个直观的理解,那么来看一下调用的时候有多简洁吧
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[SNViewStyleFactory buttonWithStyle:SNButtonSettingStyleBorderRounded targetButton:btn];
UITableViewCell *cell = [SNViewCreateFactory cellWithStyle:SNCellStyleSystemDefault forTableView:tableView];
demo示例就写这么多吧,感觉应该已经表述清楚了吧。
-----------------------华丽的分割线-----------------------------------------------
总结
优点:
- 统一管理控件的创建和样式设置可以大幅度降低了后加入的人员参与开发时,寻找可复用控件的成本,降低相同功能控件,多人写多个的可能性。
- 统一管理控件的样式设置可以保证不同位置,相同控件的样式完全一样,避免设置代码散落在项目中的各个角落。提高设置代码的复用性。
- 统一管理,统一入口,实现采用类别处理,使代码整洁的同时可读性更高。
遇到的问题
Q:项目较大情况下,这两个工厂类可能会变得无比庞大,查找起来会有一定的困难,如何缓解?
A:由于项目逐渐迭代,功能逐渐增加的原因造成共用类变胖的情况很常见,建议进行分类(category)处理。同理,分类一样适合我们的工厂类。
Q:实际项目开发中,有很多控件是完全自定义的,针对这一类的控件,如何做统一处理?
A:与朋友探讨过类似的问题,得出的结论大概是:是否为高度自定义与工厂类无关,工厂只做入口整理。而自定义控件具体创建/设置上则并不建议用类别,因为可能你用一个view同时做了一个假button,一个假segmentControl等等,这时用view的类别就不是很合适,可能一个单纯的object,使用时传入目标控件,会显得更合适一些
PS
欢迎大家一起讨论,我会定期把问题总结到QA中。
网友评论