Split View Controller在应用的中的若干问题及解决

Split View Controller在应用的中的若干问题及解决,第1张

概述Split View Controller是iPad中最具特色的视图控制器之一。它充分利用iPad横竖屏转换时的屏幕空间变化,提供了以左右分栏或popover来进行导航的界面视图。但它在使用上的复杂程度远不是TableView Controller之类的控制器所能相比。而且由于其本身所具有的限制,我们无法象使用其他控制器组件一样任意使用它。本文总结了iPad应用中SplitView Control

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在应用的中的若干问题及解决所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/1092526.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存