iOS中复杂UI模块的设计
复杂问题的解决方法
复杂问题的解决方法就是分解,将复杂性一步步分解,复杂问题分解成一系列复杂性较低的可接受的小问题。
开发拥有复杂UI的模块,关键的思路也是分解。
为了更形象,我们拿切土豆丝这件事作为比喻。厨师是怎么切土豆丝的?
完整的土豆通常因为太大,难以吃下,所以我们最常见的土豆的食品,在中国是土豆丝,在国外估计是薯条,其实都是土豆丝。土豆丝的切法,一般来说是先要切片,然后切丝。从两个方向的切,刀的轨迹交织成一个网格,网格里每一个网眼,就是一根土豆丝。
编写程序也一样,对于复杂的功能,需要分解成一根根代码的土豆丝。而且分解的过程也很神奇的和切土豆丝类似,先切片,也就是分层,然后再切丝,也就是划分模块。
1. 切片和分层
iOS开发中的切片,就是分层,常见的是按照MVC(最近还有mvvm什么的)来划分。
控制器:UIViewController的各种子类 ↓(依赖下面的两个层)
视图: UIView子类,Storyboard,Xib文件等
模型:各种表示业务逻辑的类,数据结构的,网络请求的,存储的等等
分层的关键,是解除双向依赖。
两个层之间的代码,上面的层依赖下面的层,下面的层不依赖上面的层;上面层会直接调用下面层中的代码,下面的层不会直接调用上面层中的代码(可以通过kvo,通知,delegate,block等技术间接发送消息)。
在MVC下,就是C层依赖V层,C层依赖M层。解除双向的依赖之后,问题的复杂度就下降了不少。双向依赖容易导致代码死循环,对象引用循环等,在业务的职责上,也容易出现踢皮球的现象。
常常有一种情况,以为自己分层了,其实没有分层,只是分模块。比如,一个登陆模块,你只是分成Login.storyboard, LoginViewController.swift, LoginModel.swift。这不见得是分层,可能只是分了模块。
//LoginViewController.swift
class LoginViewController:UIViewController{
var loginModel=LoginMode() //控制器
@IBAction func onLoginButton(button:UIButton!){
model.login(nameTextField.text,passwordTextField.text,self)
}
func loginSucess(){
//登陆成功之后界面的跳转
}
}
//LoginModel.swift
class LoginModel {
func login(name:String,password:String,viewController:LoginViewController) {
if checkLoginInfo(name,password) == true {
viewController.loginSucess()
}
}
}
因为model和controller发生了相互调用,M和C之间相互依赖了,所以不算分层。分层的写法是这样:
//LoginViewController.swift
class LoginViewController:UIViewController,LoginDelegate{
var loginModel=LoginMode() //控制器
@IBAction func onLoginButton(button:UIButton!){
model.login(nameTextField.text,passwordTextField.text,self)
}
func onLoginSuccess(){
self.loginSucess()
}
func loginSucess(){
//登陆成功之后界面的跳转
}
}
//LoginDelegate.swift
//使用协议,来解除模型对控制器的依赖
protocol LoginDelegate:class {
func onLoginSuccess()
}
//LoginModel.swift
class LoginModel {
weak var delegate:LoginDelegate? //利用代理模式,解除了依赖。
func login(name:String,password:String) {
if checkLoginInfo(name,password) == true {
if let d=delegate {
d.loginSucess()
}
}
}
}
2. 切丝和功能模块的分解
功能模块的分解,有两个标准,一个是从空间上,一个是从时间上。
假设有这样一个功能,要在一个view里,展示一个班级的信息,包括班级的名称,成立时间,人数,人员列表(可能是学生,也可能是老师),选中人员,展示人员的信息(包括老师的,学生的)
![](https://img.haomeiwen.com/i47832/db3edbcaf6281d59.png)
这样一个功能的UI,从空间上可以分解成:
- 班级信息
- 人员列表
- 人员详细信息
三部分界面
其中,人员详细信息部分的界面,从时间上又可以分为: - 学生详细信息
- 教师详细信息
那么,最终我们分解出的结果如下:
1. 视图层
storyBoard里面包括:
总视图:(对应控制器:SchoolClassViewController)
界面分解我喜欢用的一个技巧,是创建一些占位的试图,到时候子视图直接加入到这些占位视图里面就可以了,子视图的frame,按照占位视图的bounds设置就可以,很方便。
![](https://img.haomeiwen.com/i47832/5304c93c77f58ccc.png)
班级信息视图:(对应控制器:ClassInfoViewController)
![](https://img.haomeiwen.com/i47832/9016cf6332e3e9c5.png)
成员列表视图:(对应控制器:MembersViewController)
![](https://img.haomeiwen.com/i47832/144a49f4f97789f2.png)
学生详细信息视图:(对应控制器:StudentViewController)
![](https://img.haomeiwen.com/i47832/e9935b5b088d8d61.png)
教师详细信息视图:(对应控制器:TeacherViewController)
![](https://img.haomeiwen.com/i47832/fd79bc13a482c509.png)
2. 控制器层:
//视图总控制器
class SchoolClassViewController:UIViewController {
@IBOutlet var classInfoViewContaner:UIView! //班级信息占位视图
@IBOutlet var membersContaner:UIView! //成员列表占位视图
@IBOutlet var memberDetailContaner:UIView! //成员详细信息占位视图
var classId=0
var model:SchoolClassModel!
func viewDidLoad(){
model=schoolClassModel(classId);
//班级信息控制器
var classInfoViewController
=ClassInfoViewController()
classInfoViewController.model
=model
self.addChildViewController(classInfoViewController)
self.classInfoViewContaner.addSubView(classInfoViewController.view)
//成员列表控制器
var membersViewController=MembersViewController()
membersViewController.model=model.memberList
self.addChildViewController(membersViewController)
self.membersContaner.addSubView(membersViewController.view)
}
func showMemberDetail(){
if(model.selectedMember is Student){
var studentViewController=StudentViewController()
studentViewController.model=model.selectedMember
self.addChildViewController(studentViewController)
self.memberDetailContaner.addSubView(studentViewController.view)
}
else if(model.selectedMember is Teacher){
var teacherViewController=TeacherViewController()
teacherViewController.model=model.selectedMember
self.addChildViewController(teacherViewController)
self.memberDetailContaner.addSubView(teacherViewController.view)
}
}
}
//班级信息控制器
class ClassInfoViewController:UIViewController{
weak var model:SchoolClass!
}
//成员列表控制器
class MembersViewController:UIViewController{
var model:[Member]!
}
//学生详细信息控制器
class StudentViewController:UIViewController{
var model:Student!
}
//教师详细信息控制器
class TeacherViewController:UIViewController{
var model:Teacher!
}
3. 模型层
class SchoolClass{
var name:String!
var createTime:NSDate!
var graduateTime:NSDate!
var memberCount:Int{
get {
return count(memberList)
}
}
var memberList=[Member]()
var selectedMember:Member!
}
class Member{
var name:String!
}
class Student:Member{
…
}
class Teacher:Member{
…
}
网友评论