前言
最近一直忙于项目的事情,偶尔看看,时不时收到APP推送的通知,提醒谁关注了我,谁喜欢了文章,谁给我私信,看看上一篇文章发表的时间已经是三个月之前了,觉得自己该写写技术文章和关注我的人分享一下最近get到的技能了,奈何产品部突然提出一个类似于淘宝商城的功能需求出来,做出了30多个界面出来,告诉我们研发部1个月多月就要做出来。
我说说我的上个月听到这个需求时的心情,当时我很懵逼,懵逼的不是一个月时间,懵逼的是我们做的项目一直是关于电动车车联网,现在要做一个类似于淘宝的商城出来?我们当前需要做的需求只是关于车辆SIM卡续费和保险相应的功能,这也是当时出差时候客户所提及的需求,真的真的我很懵逼。产品是这样解释的:做车辆SIM卡续费和保险相应的功能不是都要涉及支付等功能吗,那还不如做一个商城,什么都有了,而且就30几个界面而已。。。
不过通过最后评估,限于各种需求说明不明确和时间不足,人员不够等问题,最终商城需求暂时停滞,改成了只做车辆SIM卡续费和保险相应的功能了。不过在这看似荒诞的过程中,我还是get到了不少技能,首先商城这个东西很多APP 开发并不采用原生的开发,结合当下热门技术react-native,我想到了混合开发,不过说实话我真的不会RN 技术,但是什么东西都是学来的,所以我起码了解到了RN的相关技术,最重要的是我get 到了如何嵌入RN 于原生的app中,并且已经实现了。
还有这周周五下班,跟项目经理同路,问了一下下周的开发计划,项目经理告诉我后台没人写啊,想想后台开发人员确实挺紧张的,我就说人员不够可以向领导赶紧提招人啊,起码保证以后开发进度不受影响啊,项目经理告诉我招个人进来两天就能写了,用不了那么久时间。
这下我又懵逼了,我们这个后台起码写了一年多了,当时开发人员至少7个,一直在做,在维护,现在只有2个,招一个人进来最快2天就能写了???项目经理又解释了一下,找人讲一下架构就可以了,就能写了。我记得去年他刚来2个月的时候,那时候后台人员是极度不足啊,当时中午吃饭聊天,我调侃到后台人员不够,暂时招不来人,说不定你可得来写啊,他当时的话我记忆犹新,“我写就写乱了,这得招人!”。我内心只能呵呵哒了。
还有前两天在一个简友的一个qq群里,听到一个哥们说,他们后台有多叼,别人传给他的数据,他都不处理,直接丢给他,让他APP 端处理,他在群里面各种抱怨。
想必所有的程序员都经历过这样事情,其实你发现你有时候主动的去沟通,去解释根本没有用,你再给产品解释商城有多复杂,在她眼里只是30几个界面,所以一定要记住,永远不要和不懂技术人去争论技术,你所要做的就是坚持需求必须明确的前提下我才能保质保量的完成和尽可能多的给自己争取时间。还有永远不要带着情绪去工作,你所需要做的就是做好自己的工作。说了那么多废话了,那就让我们开始本文的正文吧。
正文
案例一
前几天经常看到简友群里面哥们问,为什么我拿到远程推送的消息进行跳转时候,跳转不了啊,群主回答到,你数nav + tabbar,还是tabbar + nav,这时候哥们回去查代码去了。现在我来谈谈程序架构 tabbar + nav,首先看看普通代码(OC)设置
//这是在一个自定义tabbarController中添加子控制器
//添加控制器
- (void)addChildVC{
HomeViewController *hvc = [[HomeViewController alloc]init];
[self setChildVCWithViewController:hvc];
LocationViewController *lvc = [[LocationViewController alloc]init];
[self setChildVCWithViewController:lvc];
LifeViewController *lfv = [[LifeViewController alloc] init];
[self setChildVCWithViewController:lfv];
SelfViewController *svc = [[SelfViewController alloc] init];
[self setChildVCWithViewController:svc];
}
- (void)setChildVCWithViewController:(UIViewController *)childVC{
UINavigationController *nc = [[UINavigationController alloc]initWithRootViewController:childVC];
[self addChildViewController:nc];
}
上面自定义tabbarcontroller
,在APPDelegate
中设置为rootViewcontroller
就可以了,这代码看上去挺好,起码APPDelegate
中干干净净的,但是你不会觉得很繁琐吗?起码你得导入4个控制器的头文件,然后你得去每一个控制器实例化,那我们升级一下代码,采用反射的方法来创建控制器
Class hvc = NSClassFromString(@"SelfViewController");
[self setChildVCWithViewController:[[hvc alloc] init]];
同理创建其他三个即可。
这样你就可以愉快的删去所以用的头文件,其实这个问题在Swift中是不存在的,swift中默认统一命名空间下的文件是共享的。其实上面代码在实际开发中其实是不合理的,但是可能还有人这么写的,勤于思考的人可能会写一个基类,在基类里面处理tabbar的隐藏于显示,对于没有开发经验的人来说,估计每push 出来就要隐藏,其实有一个一劳永逸的办法就是:
自定义导航控制器,重写
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
这个方法
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
//判断如果不是栈底控制器,push的话就隐藏
if (self.childViewControllers.count > 0) {
self.tabBarController.hidesBottomBarWhenPushed = YES;
}
}
升级完以上代码其实觉得还是不够好,可扩展性不太好,我们能否采用另外的方式去实现,为了方便演示,我采用swift,跟OC思路是一样的,swift 写起来能更快一点
///设置所有子控制器
fileprivate func setupChildControllers(){
let array = [
["clsName": "MainViewController","title":"首页","imageName":"home"],
["clsName": "LocationViewController","title":"定位","imageName":"location"],
["clsName": "LifeViewController","title":"生活","imageName":"life"],
["clsName": "ProfileViewController","title":"我的","imageName":"profile"]
]
var arrayM = [UIViewController]()
for dict in array {
arrayM.append(controller(dict: dict))
}
//设置tabbarController 的子控制器
viewControllers = arrayM
}
private func controller(dict: [String: String]) -> UIViewController{
//1.取得字典的内容
guard let clsName = dict["clsName"],
let title = dict["title"],
let imagname = dict["imageName"],
//swift涉及命名空间的概念,通过反射的方法必须拿到其命名空间,这里的nameSpace是定义了一个获取命名空间的计算型属性
let cls = NSClassFromString(Bundle.main.nameSpace + "." + clsName) as? UIViewController.Type
else {
return UIViewController()
}
//2.创建视图控制器
let vc = cls.init()
vc.title = title
vc.tabBarItem.image = UIImage(named: "tabbar_" + imagname )
vc.tabBarItem.selectedImage = UIImage(named: "tabbar_" + imagname + "_selected")
vc.tabBarItem.setTitleTextAttributes([NSForegroundColorAttributeName:UIFont.systemFont(ofSize: 20)], for: .highlighted)
//自定义的导航控制器 NavController
let nav = NavController(rootViewController: vc)
return nav
}
//给Bundle扩展了nameSpace的计算型属性
extension Bundle{
var nameSpace:String{
return Bundle.main.infoDictionary?["CFBundleName"] as? String ?? ""
}
}
以上代码你会发现可扩展性一下子提高了很多,例如你们的产品汪说你看看淘宝每次到情人节什么的,它下面的主题就换了,你就可以说这个小case我分分钟种就给你实现了,这时候你只需要跟你们后台协调一下,让他给你返回一个类似于上面格式的数据就可以了,具体的业务逻辑如下:
通过以上的分析,会发现tabbar + nav 的方式的架构设计会非常方便,这样当你接收到远程通知,拿navigationController
push
的时候的时候无比的顺畅。
案例二
UIButton *completeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
completeBtn.frame = CGRectMake((SCREEN_WIDTH-(SCREEN_WIDTH*2)/5)/2, _mainView.y+_mainView.height+20, (SCREEN_WIDTH*2)/5, 40);
[completeBtn setBackgroundColor:COLOR_LIGHTREDCOLOR];
[completeBtn setTitle:@"注销" forState:UIControlStateNormal];
[completeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[completeBtn addTarget:self action:@selector(completeBtnClick) forControlEvents:UIControlEventTouchUpInside];
completeBtn.titleLabel.font = [UIFont systemFontOfSize:14];
completeBtn.clipsToBounds = YES;
completeBtn.layer.cornerRadius = 2;
[self.view addSubview:completeBtn];
在我接手现有的项目的时候,在创建按钮地方就出现这一大坨代码,越看越不舒服,如果你一个项目中这样写代码估计会被人骂爹 的,我们能不能写的优雅一点,我们升级一下代码:
+ (instancetype)buttonWithFrame:(CGRect)frame CornerRadius:(CGFloat)cornerRadius BorderColor:(UIColor *)borderColor BorderWidth:(CGFloat)borderWidth Title:(NSString *)title Alpha:(CGFloat)alpha TitleLabelFont:(CGFloat)font TitleColor:(UIColor *)titleColor BackgroudColor:(UIColor *)backgroudColor{
UIButton *btn = [[self alloc] init];
btn.frame = frame;
btn.layer.cornerRadius = cornerRadius;
btn.layer.masksToBounds = YES;
if (borderWidth) {
btn.layer.borderWidth = borderWidth;
}
if (borderColor) {
btn.layer.borderColor = borderColor.CGColor;
}
[btn setBackgroundColor:backgroudColor];
[btn setTitle:title forState:UIControlStateNormal];
if (alpha) {
btn.alpha = alpha;
}
btn.titleLabel.font = [UIFont systemFontOfSize:font];
[btn setTitleColor:titleColor forState:UIControlStateNormal];
return btn;
}
我们给UIButton
扩展一个方法,这样需要创建的地方,我们直接就可以获取。所以在一个项目中抽出公有的类是非常有必要的。
最后我谈一点我个人的理解,在项目多使用如基类的思想,分类思想,对于一个复杂界面(代码量超过800行),建议使用MVVM架构思想,将网络层提取出来,对controller进行相应的解耦,这样会让你代码的耦合性非常低,相应的可扩展性变高,平时多阅读以下开源的代码,看看大神的编写的思路,以及架构思想,多模仿多练习,不久你的思想认识,以及编写代码的能力会有很大的提升,最后说一下关于阅读外文资料看法,其实对于还没有相应阅读能力的朋友,没必要去强迫自己去看,因为你花费半天时间去看外文资料,有一半以上的时间去查单词是没有意义的,本来普通的搜索引擎是可以满足相应的要求的,等自己发现现有的资料无法满足自己的开发需求,可以去查找相应的资料,stackFlow
gogle
等等都是不错的选择。