前言
最近公司需要开发iPad版的应用程序,项目中要实现分屏控制器的功能,之前有了解到使用UISplitViewController可以实现分屏的功能,当时查了查资料,感觉挺简单的,但是随着研究的深入,发现并不是那么容易的,由于网上关于UISplitViewController的使用教程并不多,自定义实现分屏功能的教程就更少了,所以接下来我将把这几天对UISplitViewController的研究和自定义实现分屏控制器的功能的Demo分享给大家,希望能帮助到有这方面困惑的朋友。
使用系统的UISplitViewController
Demo中的第一个target是使用的UISplitViewController,通过storyboard实现,系统的UISplitViewController挺简单的,实现步骤:
- 在storyboard中添加一个UISplitViewController控制器,给DetailViewController包装一个导航控制器,这里在连线的时候要注意,要和splitViewController建立关系,选择detail view controller。
2.新建一个控制器MainViewController继承自UISplitViewController,MasterTableViewController继承自UITableViewController,DetailViewController继承自UIViewController,并把相关控制器和storyboard中的相关联。
3.在MasterTableViewController中编写一个协议,主要思路就是MainViewController成为MasterTableViewController的代理,实现协议方法,从而和DetailViewController交互。
@protocol masterTableViewControllerDelegate <NSObject>
- (void)masterTableViewController:(MasterTableViewController *)masterVC didSelectedRow:(SingleModel *)singleModel;
@end
在MasterTableViewController中的tableView行点击事件中,调用代理方法,传递数据
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if ([self.delegate respondsToSelector:@selector(masterTableViewController:didSelectedRow:)]) {
GroupModel *groupModel = self.groupModel[indexPath.section];
[self.delegate masterTableViewController:self didSelectedRow:groupModel.typeList[indexPath.row]];
}
}
在MainViewController中,设置当前控制器为MasterTableViewController的代理,实现协议方法
- (void)viewDidLoad {
[super viewDidLoad];
//获取到导航控制器对象
UINavigationController *masterNav = [self.childViewControllers firstObject];
//获取到MasterTableViewController的对象
MasterTableViewController *master = [masterNav.childViewControllers firstObject];
master.delegate = self;
}
/**
* masterTableViewController的代理方法
*/
- (void)masterTableViewController:(MasterTableViewController *)masterVC didSelectedRow:(SingleModel *)singleModel{
UINavigationController *detailNav = [self.childViewControllers lastObject];
DetailViewController *detail = [detailNav.childViewControllers firstObject];
detail.singleModel = singleModel;
[detailNav popToRootViewControllerAnimated:YES];
}
重写DetailViewController的属性singleModel的setter方法就能实现和MasterTableViewController交互了
- (void)setSingleModel:(SingleModel *)singleModel{
_singleModel = singleModel;
self.nameView.text = singleModel.subjectName;
}
通常情况下,详情界面也会是一个tableView,在重写的singleModel中获取到上一界面传来的值,让当前控制器的tableView reloadData就可以了。
自定义实现分屏的功能
这个是今天要介绍的重点,因为有的时候系统的UISplitViewController并不能满足项目的需求,UISplitViewController只能做为程序窗口的根视图,当程序中需要使用UITabBarController或者UINavigationController来切换到分屏控制器的时候,并不能到达我们的目的。
可以看一下官网对UISplitViewController的介绍
主要的一个截图
大体的意思就是分屏控制器不能进入导航的栈中(也就是不能通过导航控制器跳转),不推荐将分屏控制器做为某个控制器的子控制器,通常是做为程序窗口的根视图。
那如果需求是: 在主界面需要跳转到分屏控制器改怎么办呢?
这里有一种思路就是在主界面放一个按钮,在按钮的点击方法里面切换window的根视图。这个不是今天介绍的重点,就不在这里讨论了。
重点是---怎么自定义控制器具有分屏显示的功能(结合UITabBarController)
思路是新建三个控制器,分别是主控制器、左侧显示的列表控制器和右侧显示的详情控制器,将列表控制器和详情控制器的View添加到主控制器的View上,让主控制器成为列表控制器的代理,实现相应的协议方法从而和详情控制器就行交互。大体的思路和上面提到的使用系统的UISplitViewController差不多,主要的不同就是没有使用UISplitViewController,而是自定义的UIViewController,主要看一下自定义控制器中的代码。
添加两个导航控制器的属性
@property (nonatomic, strong) UINavigationController *masterNav;
@property (nonatomic, strong) UINavigationController *detailNav;
在viewDidLoad中初始化列表控制器和详情控制器,并添加到主控制界面上。
- (void)viewDidLoad {
[super viewDidLoad];
// self.navigationController.navigationBarHidden = YES;
HZYMasterViewController *masterVC = [HZYMasterViewController new];
self.masterNav = [[UINavigationController alloc] initWithRootViewController:masterVC];
[self addChildViewController:self.masterNav];
[self.view addSubview:self.masterNav.view];
HZYDetailViewController *detailVC = [HZYDetailViewController new];
self.detailNav = [[UINavigationController alloc] initWithRootViewController:detailVC];
[self addChildViewController:self.detailNav];
[self.view addSubview:self.detailNav.view];
masterVC.delegate = self;
}
实现协议方法
- (void)masterViewController:(HZYMasterViewController *)masterVC didSelectedRowInSection:(NSInteger)section{
HZYDetailViewController *detailVC = [self.detailNav.childViewControllers firstObject];
[self.detailNav popToRootViewControllerAnimated:YES];
//向detailVC传递点击的Section用来区分detailVC中tableViewCell的显示样式
[detailVC putSection:section];
}
核心的方法,当控制器的View的子视图重新布局时调用,用来布局子视图。
- (void)viewWillLayoutSubviews{
[_masterNav.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@64);
make.left.equalTo(@70);
make.width.equalTo(kMasterWidth);
make.bottom.equalTo(@0);
}];
[_detailNav.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(_masterNav.view);
make.right.equalTo(self.view.mas_right);
make.left.equalTo(_masterNav.view.mas_right);
}];
}
DetailViewController在putSection方法的实现中获得index,工具类通过index获得相应模型数据,刷新tableView
- (void)putSection:(NSInteger)section{
_section = section;
__weak typeof (self) weakSelf = self;
//获取点击Section的模型数据,为了方便这里每个Section中的行都是一个模型数据,如果获取网络数据的话要更改工具类中的方法,视情况而定!
[Tool getDataWithSection:section successBlock:^(NSMutableArray *modelArray) {
weakSelf.modelArray = modelArray;
[weakSelf.tableView reloadData];
}];
}
Demo介绍
Demo分为两个target,一个是通过系统UISplitViewController结合storyboard实现的,一个是自定义控制器纯代码实现的。
工程中添加两个target的遇到的坑
之前并没有在一个工程中添加过两个target,这次在两个target下coding遇到最多的问题就是
一般这种问题就是编译的问题,找不到编译的源文件,解决方法就是在工程的target->Build Phases->Compile Sources,添加报错的.m文件,重新编译就OK了!
为什么会产生这种错误呢?主要就是在新建类的时候没有选中相应的target
还有一种解决方案,选中报错的.m文件,展开实用工具(配合IB实用的那个栏目),点击第一个(file inspector),找到下面的Target Membership,勾选上相应的target。
结语:
第一次在简书上面发表文章,希望我这几天的研究能够帮助到有这方面困惑的朋友,谢谢大家的支持。
最近很欣赏薛之谦说的一句话:在这个时代根本就没有怀才不遇, 关键是你真的必须具备才华 所以请你一定要强大自己!对于我们iOS开发来说,市场已经进入饱和期了(甚至有人调侃为iOS烂大街),很多人抱怨工作不好找,现在的市场需要的不再是只会写写界面,只会上网复制粘贴代码的开发者,所以你一定要强大自己,等你足够强大了,好的工作自然就来了!