Split VIEw Controller是iPad中最具特色的视图控制器之一。它充分利用iPad横竖屏转换时的屏幕空间变化,提供了以左右分栏或popover来进行导航的界面视图。但它在使用上的复杂程度远不是tableVIEw Controller之类的控制器所能相比。而且由于其本身所具有的限制,我们无法象使用其他控制器组件一样任意使用它。本文总结了iPad应用中SplitVIEw Controller的一些问题及适用的解决方法,希望能起到抛砖引玉的效果。
一、从IB构建SplitVIEw Controller
对于Split VIEw Controller来说,通过代码来使用是比较简单的,因此不用多讲,相信大家并不陌生。但在IB中构建SplitVIEw Controller,尚未有人介绍。虽然在笔者另一篇博文“iPad开发:UISplitVIEwController应用”曾有过介绍,但那是在Xcode3.2下实现的,随着Xcode已经升级至4.2,笔者觉得有必要再次罗嗦一番。
新建3个VIEw Controller类:iPadHelpVC、iPadHelpIndexVC、iPadHelpContentVC,注意勾选“WithNib…”。
1、iPadHelpVC
这是一个普通的VIEw Controller,但我们在其中拖入了一个SpliteVIEw Controller组件:
VIEw对象是一个空白UIVIEw。它不包含实质的内容。我们在使用iPadHelpVC类时,主要是为了使用它的Xib文件中的SplitVIEw Controller对象,因此这个VIEw对象只是个摆设。
提示:你不能删除VIEw对象,因为它会导致一个IB对象连接错误——因为VIEw Controller的vIEw属性必须连接到一个UIVIEw。
重要的是Split VIEw Controller对象。它下面会自动包含一些子对象:一个NavigationController、一个VIEw Controller。在Navigation Controller下面又包括一个Navigation bar和一个tableVIEw Controller。
下面我们要对这些对象进行连接。
将table VIEw Controller的IDentity Class修改为iPadHelpIndexVC,待会我们要用它来提供SplitVIEw Controller左边的导航列表。
将VIEw Controller得IDentifty Class修改为iPadHelpContentVC,待会我们用它来提供SplitVIEw Controller右边的内容视图。
在iPadHelpVC类中声明一个出口:
@property (nonatomic,retain) IBOutlet UISplitVIEwController *splitVC;
⋯⋯
@synthesize splitVC;
将Split VIEw Controller对象和这个splitVC出口连接起来,便于我们在Xcode中引用。
在iPadHelpVC的vIEwDIDLoad方法中:
// Split VIEwController 只能作为window的根视图控制器
SplitDemoDelegate*app=(SplitDemoDelegate*)[[UIApplication sharedApplication]delegate];
app.stubVC=app.window.rootVIEwController;
app.window.rootVIEwController=splitVC;
SplitDemoDelegate是我们这个示例程序的应用程序委托类。我们在这个类中定义了一个顶层对象stubVC:
@property(retain,nonatomic)UIVIEwController* stubVC;
⋯⋯
@synthesize stubVC;
提示:关于顶层对象,简单地说就是app 全局对象。参考另一篇博文: 单例,应用程序委托和顶层数据。
我们需要先在stubVC中保存一份window.rootVIEwController的引用。因为SplitVIEw Controller只能在window对象的rootVIEwController上应用。如果我们不想让app从头至尾只使用一个Split VIEwController的话,我们需要保持住导航到Split VIEw Controller之前的那个视图控制器(也许是一个VIEw Controller,也许是一个NavigationController)。这样我们可以从Split VIEw Controller再次导航回前面的VIEw Controller。
上面的工作做完后,在IB的Objects面板显示如下:最后,我们需要将Split VIEw Controller的delegate和I PadHelp IndexVC进行连接。这样可以在iPadHelpIndexVC类中加入一些代码,定制Split VIEw Controller的行为。
选择Split VIEw Controller对象,在Connections面板中将delegate右边的圆圈拖到I Pad Help IndexVC对象:2、iPadHelpIndexVC
这个类提供了左边栏的导航列表。一般来说,它应该是UItableVIEw子类。它会自带一个tableVIEw对象,并实现UItableVIEwDataSouce和UItableVIEwDelegate协议。用于Split VIEw Controller的delegate是iPadHelpIndexVC,因此还需要声明实现UISplitVIEwControllerDelegate协议:
@interface iPadHelpIndexVC :UItableVIEwController
<UISplitVIEwControllerDelegate,UItableVIEwDelegate,UItableVIEwDataSource>{
说明一个数组作为table vIEw对象的数据模型:
NSArray*model;
声明一个出口,用于和Split VIEw Controller进行连接:
@property (nonatomic,assign) IBOutlet UISplitVIEwController*splitVIEwController;
⋯⋯
@synthesize popoverController;
然后回到iPadHelpVC.xib,将出口splitVIEwController和SplitVIEw Controller对象连接在一起:
在iPadHelpIndexVC的vIEwDIDiLoad中,我们初始化model并在model中添加一些数据:
model=[[NSArray alloc]initWithObjects:@"文档1",@"文档2",nil];
接下来,我们先实现table VIEw的DataSource 方法:
- (NSInteger)numberOfSectionsIntableVIEw:(UItableVIEw *)tableVIEw
{
return 1;
}
- (NSInteger)tableVIEw:(UItableVIEw *)tableVIEwnumberOfRowsInSection:(NSInteger)section
{
return model.count;
}
- (UItableVIEwCell *)tableVIEw:(UItableVIEw *)tableVIEwcellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static Nsstring *CellIDentifIEr = @"CellIDentifIEr";
UItableVIEwCell *cell = [self.tableVIEw dequeueReusableCellWithIDentifIEr:CellIDentifIEr];
if (cell == nil) {
cell = [[[UItableVIEwCell alloc] initWithStyle:UItableVIEwCellStyleDefault reuseIDentifIEr:CellIDentifIEr] autorelease];
}
cell.textLabel.text = [model objectAtIndex:indexPath.row];
return cell;
}
当用户点击左边栏导航列表中的条目,我们修改右边栏的内容显示:
- (voID)tableVIEw:(UItableVIEw *)tableVIEwdIDSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
iPadHelpContentVC <DetailVIEwController>*detailVIEwController = nil;
detailVIEwController= [[iPadHelpContentVC alloc] initWithNibname:@"iPadHelpContentVC" bundle:nil];
// 修改 split vIEw controller的vIEwControllers属性.
NSArray *vIEwControllers = [[NSArray alloc] initWithObjects:self.navigationController,detailVIEwController,nil];
splitVIEwController.vIEwControllers =vIEwControllers;
detailVIEwController.lbTitle.text=[model objectAtIndex:indexPath.row];
[vIEwControllersrelease];
// 如果popover窗口在d出中,解散
if (popoverController!= nil) {
[popoverController dismisspopoverAnimated:YES];
}
// 重新设置右边栏的popover按钮
if (rootPopoverbuttonItem!= nil) {
[detailVIEwController showRootPopoverbuttonItem:self.rootPopoverbuttonItem];
}
[detailVIEwControllerrelease];
}
这个方法中用到的两个属性: popoverController 和 rootPopoverbuttonItem声明如下:
@property (nonatomic,retain) UIPopoverController*popoverController;
@property (nonatomic,retain) UIbarbuttonItem*rootPopoverbuttonItem;
⋯⋯
@synthesize popoverController;
@synthesize rootPopoverbuttonItem;
协议DetailVIEwController 声明了两个必须由iPadHelpContentVC实现的方法:
@protocol DetailVIEwController
- (voID)showRootPopoverbuttonItem:(UIbarbuttonItem*)barbuttonItem;
- (voID)invalIDateRootPopoverbuttonItem:(UIbarbuttonItem*)barbuttonItem;
@end
这两个方法在iPad方向转变为竖屏和横屏时调用。
最后,我们需要在iPadHelpIndexVC中实现Split VIEwController的委托方法。
// 本方法用于竖屏时d出popover
- (voID)splitVIEwController:(UISplitVIEwController*)svcwillHIDeVIEwController:(UIVIEwController *)aVIEwControllerwithbarbuttonItem:(UIbarbuttonItem*)barbuttonItemforPopoverController:(UIPopoverController*)pc {
// 从参数获得按钮和popover controller的引用.
barbuttonItem.Title = @"文档";
self.popoverController =pc;
self.rootPopoverbuttonItem = barbuttonItem;
// 获取右边栏,在右边栏中显示按钮
UIVIEwController<DetailVIEwController> *detailVIEwController = [splitVIEwController.vIEwControllers objectAtIndex:1];
[detailVIEwControllershowRootPopoverbuttonItem:rootPopoverbuttonItem];
}
// 本方法用于横屏时显示左边栏并消除popover按钮
- (voID)splitVIEwController:(UISplitVIEwController*)svcwillShowVIEwController:(UIVIEwController *)aVIEwControllerinvalIDatingbarbuttonItem:(UIbarbuttonItem *)barbuttonItem {
// splite vIEw controller的vIEwControllers属性管理了两个VIEw Controller:左边栏、
// 右边栏,它们分别用索引0和1访问。
UIVIEwController<DetailVIEwController> *detailVIEwController =[splitVIEwController.vIEwControllers objectAtIndex:1];
// 清除popover按钮(根据DetailVIEwController协议)
[detailVIEwControllerinvalIDateRootPopoverbuttonItem:rootPopoverbuttonItem];
// 释放
self.popoverController =nil;
self.rootPopoverbuttonItem = nil;
}
3、iPadHelpContentVC
这个类,很简单,我们也不准备实现实质性的功能,仅仅是在工具栏的Label上显示菜单的标题。因此它仅包含了一个Toolbar和一个Label对象:
这两个对象都需要相应出口进行连接:
@property (nonatomic,retain) IBOutlet UIToolbar *toolbar;
@property (nonatomic,retain)IBOutlet UILabel* lbTitle;
⋯⋯
@synthesize toolbar;
@synthesize lbTitle;
然后我们把它们连接在一起:
根据iPadHelpIndexVC中的介绍,iPadHelpContentVC类是需要实现DetailVIEwController协议的:
@interface iPadHelpContentVC : UIVIEwController
<DetailVIEwController>
⋯⋯
#pragma markDetailVIEwController 协议实现
- (voID)showRootPopoverbuttonItem:(UIbarbuttonItem *)barbuttonItem {
// 在工具栏上加一个popover按钮,用于d出导航列表
NSMutableArray*itemsArray = [toolbar.items mutablecopy];
[itemsArray insertObject:barbuttonItem atIndex:0];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
- (voID)invalIDateRootPopoverbuttonItem:(UIbarbuttonItem*)barbuttonItem {
// 横屏显示时,将popover按钮移除
NSMutableArray*itemsArray = [toolbar.items mutablecopy];
[itemsArray removeObject:barbuttonItem];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
二、在RootVIEwController中调用SplitVIEwController
假设我们的程序并不是一来就显示Split VIEw Controller,那么我们需要将window的rootVIEwController设置为SplitVIEw Controller对象。这个工作其实已经在iPadHelpVC类的vIEwDIDLoad中做了,因此我们只需要把iPadHelpVC当做普通的VIEwController来显示就可以了。你可以用presentModalVIEw或者pushVIEwController显示SplitVIEw Controller:
iPadHelpVC* helpVC=[[iPadHelpVC alloc]initWithNibname:@"iPadHelpVC"
bundle:nil];
[self.navigationController pushVIEwController:helpVC animated:YES];
注意:由于vIEwDIDLoad只会在initWithNibname方法中调用,因此每次显示Split VIEw Controller时你必须调用initWithNibname方法重新初始化helpVC,否则SplitVIEw Controller不能显示(这跟Tab bar Controller是一样的)。
三、从SplitVIEwController返回
我们的app存在多个VIEw Controller(起码两个,一个Split VIEwController和一个其他的VIEw Controller),并且Split VIEw Controller并不是第一个控制器,因此我们必须考虑如何从SplitVIEw Controller返回第一个视图的问题。
我们首先决定在Split VIEw Controller的右边栏加一个返回按钮。原因很简单,因为左边栏在竖屏时不显示,而右边栏无论横屏竖屏总是显示。
打开iPadHelpContentVC.xib,在工具栏上放一个bar buttonItem,并让它和相应的IBAction连接:
-(IBAction)backAction;
⋯⋯
-(voID)backAction{
DLTAppDelegate* app=(DLTAppDelegate*)[[UIApplication sharedApplication]delegate];
app.window.rootVIEwController=app.stubVC;
UINavigationController* nc=(UINavigationController*)app.stubVC;
[nc popVIEwControllerAnimated:YES];
}
这里,我们重新把window的rootVIEwController设置回原来的Controller。
提示:你可能奇怪这个stubVC是什么时候保存的。 这是在iPadHelpVC的vIEwDIDLoad方法中:
SplitDemoDelegate* app=(SplitDemoDelegate*)[[UIApplication sharedApplication]delegate];
app.stubVC=app.window.rootVIEwController;
此外,最后一句“[nc popVIEwControllerAnimated:YES];”稍微显得有些奇怪。因为iPadHelpVC本身还是一个VIEwController(它还有一个无用的vIEw属性),当你pushVIEwController时,实际上把这个带有空白VIEw的iPadHelpVC压入navigationController的栈中了。当你恢复rootVIEwController时,自然将压入栈顶的空白VIEw显示出来了。如果你去掉最后的这句,当从SplitVIEw Controller返回原根视图时,会返回iPadHelpVC的这个VIEw界面(空白窗体,但带一个Navigation bar)。而此时你必须点击Navigationbar上的“返回”按钮才能返回根视图。
四、一个BUG
当你运行程序,你会发现如下BUG:
BUG描述:每当你d出一次popover菜单并选择其中一项,则popover按钮(即文档按钮)会往右边移动一点位置。对比第1张和第3张截图,你会发现popover按钮的位置往右移动了约一个barItem的距离。重复上述动作,popover按钮会不断右移,直到不可见。
这个问题在竖屏时出现。在iPadHelpContentVC中,我在工具栏中放入了一个FlexibleSpace bar button Item和一个bar button Item,以便在右边栏中显示退出按钮:这就会导致上面的BUG出现。
暂时想到的解决办法是不要在xib中放入任何bar button Item,而改用代码动态生成所有bar button Item:
- (voID)showRootPopoverbuttonItem:(UIbarbuttonItem *)barbuttonItem {
// Add the popover button to thetoolbar.
NSMutableArray *itemsArray = [[NSMutableArray alloc]init];
[itemsArray addobject:barbuttonItem];
UIbarbuttonItem* item=[[UIbarbuttonItem alloc]
initWithbarbuttonSystemItem:UIbarbuttonSystemItemFlexibleSpace
target:nil action:nil];
[itemsArray addobject:item];
[item release];
item=[[UIbarbuttonItem alloc]
initWithTitle:@"返回"
style:UIbarbuttonItemStylebordered
target:self action:@selector(backAction)];
[itemsArray addobject:item];
[item release];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
总结以上是内存溢出为你收集整理的Split View Controller在应用的中的若干问题及解决全部内容,希望文章能够帮你解决Split View Controller在应用的中的若干问题及解决所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)