浅谈 : iOS工程中哪些需要建立基类(MVC)
什么是基类
首先我们来看一下基类的定义:通过继承机制,能够利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,并且还同一时候拥有旧的成员。
基类的好处
首先在代码上面肯定是减少了代码量, 让程序瘦身,其次能够使代码的逻辑更加清晰等。不过,切记基类不能滥用,否则需求改变, 就可能面临程序的很多地方都需要修改。个人觉得创建基类的原则之一应该有: 确定了这个代码肯定肯定不会随着需求的改变而改变或者改动非常小。事实上,有时候在项目的功能已经实现了之后,再来进行很多代码的整理也是个不错的选择,这样也能够提升自己的经验。貌似这就会涉及到重构的理念了。
哪些需要创建基类
以下我将列举基本每个项目都需要创建的基类,可能不够全面,希望您能给予补充。
一、Controller
1. UITabBarController
a.设置tabBar样式;
b.初始化tabBarController控制的 controllers;
c.签订UITabBarControllerDelegate,实现协议方法,例如在用户第二次点击tabItem的时候,需要刷新界面的情况。
d.注册tabItem切换时候的消息等
- (void)initControllers
{
NSArray *tabControllerNames = @[@"HomePageViewController",
@"SearchViewController",
@"UserViewController"];
NSMutableArray *tabControllers = @[].mutableCopy;
[tabControllerNames enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *controllerName = obj;
Class class = NSClassFromString(controllerName);
if (class) {
[tabControllers addObject:[class new]];
}
}];
self.viewControllers = tabControllers;
}
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[RefreshViewController class]]) {
RefreshViewController *contrller = (RefreshViewController *)viewController;
[contrller startRefresh];
}
return YES;
}
2. UINavigationController
a.设置navigationBar样式;
如果我们在整个工程中创建的是多个UINavigationController, 那么在自定义的tabBar中, 就会初始化多个UINavigationController。 如果我们把设置样式写在了自定义的NC的viewDidLoad 里面, 虽然我们设置的样式是一样的, 却会走这个方法好几次, 所以, 建议大家在NC中使用+ (void)initialize 这个类方法, 在这个方法里面订制样式。这个类方法在整个app 的运行中, 不管你调用当前这个类多少次, 都会只走一次, 能够提高程序的效率。appearanceWhenContainedIn这个方法里面的class就是指定你想统一样式的那些类。但是需要注意的是 , 如果你创建了ABNavigationController这个的子类, 那么这个initialize方法就会走多次。 所以 记得进行判断:
+ (void)initialize {
if (self == [ABNavigationController class]) {
UINavigationBar *bar = [UINavigationBar appearanceWhenContainedIn:[ABNavigationController class], nil];
[bar setBackgroundImage:[UIImage imageNamed:@"1"] forBarMetrics:UIBarMetricsDefault];
[bar setTitleTextAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:20],
NSForegroundColorAttributeName : [UIColor greenColor]}];
}
}
如果在项目中有遇到不同的样式, 那么可以在初始化UINavigationController的地方判断。例如我的ABAreaViewController不想用上面那种样式, 就可以像下面这样写了。
// 获取nv
UINavigationController *nv = [[ABNavigationController alloc] initWithRootViewController:vc];
if ([vc isKindOfClass:[ABSpecialViewController class]]) {
nv = [[UINavigationController alloc] initWithRootViewController:vc];
return nv;
}
b. 设置返回手势
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = YES;
}
NSLog(@"%@ did show", [viewController.class description]);
}
3. UIViewController
a. 界面的背景色;
b. 消息提示的方法: 加载成功\失败,提交成功\失败,网络异常等
c. push controller的封装等
4. 需要刷新|数据请求的UIViewController
这个需要继承👆的UIViewController,顾名思义, 需要在这里面写添加addRefresh\startRefresh等。
5. 带有tableView/collectionView布局的
这个需要继承👆 。可以先通过懒加载或者一个方法初始化了tableView/collectionView,设置了基本参数和实现了基本的协议方法。子类作修改的时候只要重写父类的方法就可以了。创建基类的时候 如果是tableview的style或者collectionview的flowlayout这种 可以写一个方法 返回tableview 或者是返回flowlayout, 像下面这种:
- (UIScrollView *)refreshScrollView
{
UITableView *tableView = [self tableView];
tableView.delegate = self;
tableView.dataSource = self;
tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[tableView registerClass:[self tableViewCellClass] forCellReuseIdentifier:@"cell"];
return tableView;
}
- (UITableView *)tableView
{
return [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
}
- (Class)tableViewCellClass
{
return [UITableViewCell class];
}
不过对于collectionView的flowLayout, 可以通过重写它的UICollectionViewDelegateFlowLayout的方法来设置参数,这个就依照个人喜好吧。但是切记:如果重写了协议方法,那么直接通过flowLayout.itemSize等这样来设置的参数就会失效了。同tableview
在controller里面的某些逻辑,我这里可能不全面,可以借鉴这里
二、UIView
1. UITableViewCell/UICollectionViewCell
a. 写一个传值的方法,子类继承的时候, 直接实现传值的方法就好。
- (void)configureWithItem:(id)item;
b. 不同的页面可能会重复的利用一种cell 或者变化甚微, 这个时候,就再创建第二个基类, 可以将控件暴露在.h文件中,如果是传值的话, 就在.m中重写父类方法;更改frame的话, 可以在layoutSubviews里面更改,别忘了[super layoutSubviews ]
2. 自定义的视图
对于tableView/collectionView头视图、轮播图、时间选择器等, 需要自定义的视图, 需要传值的, 可以像cell一样,至少先写一个传值的方法备着。其他的视情况而定。还有带有tableView/collectionView布局的,也可以先实现协议方法,等待子类重写。
3. 系统控件
对于label\button等, 做夜间模式的话,需要写基类。如果你想代码更加简洁,不妨向下面一样,把原有方法再封装一下,使用起来更简单。
- (void)setNormalImage:(UIImage *)image
{
[self setImage:image forState:UIControlStateNormal];
}
[button setNormalImage:ImageNamed(@"icon_back")];
三、NSObject
1. API
除了某些参数不同之外, 其他需要提供给服务器的参数很多都一致,
POST请求的话, 设置字典的参数;GET请求的话,设置拼接的字符串。在API基类里面写数据请求的方法, 缓存, 清理缓存的方法。
2. model类
数据解析的时候,如果需要创建model类,这个时候, 如果刚好有些属性以及容错方法都是重复率比较高的,这个时候也可以创建一个基类。但是最好确定每个model都有这个属性再建立基类,否则我建议就写容错方法吧。借鉴这里
彩蛋 推荐学习网址
唐巧 :被误解的MVC和被神化的MVVM
RAC 核心元素与信号流
琪一可原创