iOS导航模式

在我们开发一款app时,我们都会在设计阶段把应用的导航确定下来。导航指导用户使用我们的应用,如果没有导航,我们的应用就会显得很混乱。


在iOS应用中,视图控制器处于重要地位。在UIKit中,视图控制器有很多种,有些负责显示视图,有些也同时兼顾导航。我们常见的视图控制器有以下几种:

1、UIViewController。用于自定义视图控制器的导航。

2、UINavigationController。导航控制器,它与UITableViewController结合使用,能够构建树形结构导航模式。

3、UITableBarController。标签栏控制器,用于构建树标签导航模式。

4、UIPageViewController。呈现电子书导航导航风格的控制器(iOS5推出)。

5、UISplitViewController。把屏幕分割成几块的视图控制器,主要为iPad屏幕设计。

6、UIPopoverController。呈现“气泡”风格视图的控制器,主要为iPad屏幕设计。


从组织形式上看,iPhone主要有3种导航模式,每一种导航模式都对应于不同的视图控制器。

1、平铺导航模式。内容没有层次关系展示的内容都放置在一个主屏幕上,采用分屏或分页控制器进行导航,可以左右或者上下滑动屏幕查看内容(如iPod自带的天气预报应用)。

2、标签导航模式。内容被分成几个功能模块,每个功能模块之间没有什么关系。通过标签管理各个模块(如新浪微博应用)。

3、树形结构导航模式。内容有层次,从上到下细分或者具有分类包含等关系(如iPod自带的邮件应用)。


当然了,在实际应用中,我们还可以将这些导航模式进行组合使用。


除了上面提到的导航模式外,我们在应用中还经常会用到模态视图。有时候我们在主任务产线中要暂时转到次要任务的产线,这个时候模态视图就派上用场了。比如,我们在登陆论坛时,如果没有账号,我们就要先注册账号,然后再登陆。


默认情况下,模态视图是从屏幕下方滑出来的。负责控制模态视图的控制器并非一个专门的类,它可以是上面的视图控制器子类。负责主任务的试图控制器与模态视图控制器之间是“父子”关系。


在UIViewController类中,主要有如下两个方法:

1、presentViewController:animated:completion 呈现模态视图。

2、dismissViewControllerAnimated:completion 关闭模态视图。


- (IBAction)register:(id)sender {
    UIStoryboard *storyboard=[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    UIViewController *registerViewController=[storyboard instantiateViewControllerWithIdentifier:@"registerViewController"];
    registerViewController.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;
    [self presentViewController:registerViewController animated:YES completion:^{
        NSLog(@"RegisterViewController present");
    }];
}


modalTransitionStyle属性是UIViewController类提供的,用于设定模态视图切换的动画效果,它们是由枚举UIModalTransitionStyle定义:

1、UIModalTransitionStyleCoverVertical。呈现时沿垂直方向由底推出,关闭时从底部消失。

2、UIModalTransitionStyleFlipHorizontal。水平翻转,呈现时从右往左翻转;关闭时从左往右翻转。

3、UIModalTransitionStyleCrossDissolve。两个视图交叉淡入淡出。

4、UIModalTransitionStylePartialCurl。呈现时模态视图卷起一个边角翻页,关闭时模态视图翻回来。

另外,模态视图的显示风格由modalPresentationStyle属性控制,该属性由UIModalPresentationStyle定义,主要包括四个常量:

1、UIModalPresentationFullScreen,全屏状态,这是默认的呈现style。iPhone只能全屏呈现

2、UIModalPresentationPageSheet,它的宽度是768点,iPad横屏时非全屏,竖屏时全屏。

3、UIModalPresentationFormSheet,尺寸固定为540x620,无论是横屏还是竖屏情况,呈现尺寸都不会变。

4、UIModalPresentationCurrentContext,表示与父视图控制器有相同的呈现方式。



一、平铺导航


1、基于分屏导航的实现


基于分屏导航是平铺导航模式的主要实现方式,主要涉及到控件有分屏控件(UIPageControl)和滚动视图(ScrollView),其中分屏控件是IOS标准控件。对于UIPageControl,屏幕的总数应该限制在20个以内,超过20就会溢出。事实上,当一个应用超过10屏,使用基于UIPageControl的平铺导航模式已经不是很方便了。


下面是一个基于UIPageControl实现的“画廊”应用。

storyboard:



//  ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;


@property (strong,nonatomic) UIView *page0;
@property (strong,nonatomic) UIView *page1;
@property (strong,nonatomic) UIView *page2;

- (IBAction)changePage:(id)sender;

@end

这里的changePage方法是对PageControl变化的响应。


//  ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self initPages];
    self.scrollView.frame=self.view.frame;
    self.scrollView.contentSize=CGSizeMake(self.scrollView.frame.size.width*3, self.scrollView.frame.size.height);
    self.scrollView.delegate=self;
    [self.scrollView addSubview:self.page2];
    [self.scrollView addSubview:self.page1];
    [self.scrollView addSubview:self.page0];
    
}

-(void)initPages
{
    //storyboard
    UIStoryboard *mainStoryboard=[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    
    //page0
    UIViewController *page0ViewController=[mainStoryboard instantiateViewControllerWithIdentifier:@"page0"];
    self.page0=page0ViewController.view;
    self.page0.frame=CGRectMake(0, 0, 320, 440);
    //page1
    UIViewController *page1ViewController=[mainStoryboard instantiateViewControllerWithIdentifier:@"page1"];
    self.page1=page1ViewController.view;
    self.page1.frame=CGRectMake(320, 0, 320, 440);
    //page2
    UIViewController *page2ViewController=[mainStoryboard instantiateViewControllerWithIdentifier:@"page2"];
    self.page2=page2ViewController.view;
    self.page2.frame=CGRectMake(320*2, 0, 320, 440);
    
}

//Tells the delegate when the user scrolls the content view within the receiver.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    //The point at which the origin of the content view is offset from the origin of the scroll view.
    CGPoint offset=scrollView.contentOffset;
    self.pageControl.currentPage=offset.x/320;
}

//Animate changes to one or more views using the specified duration.
- (IBAction)changePage:(id)sender {
    [UIView animateWithDuration:0.3 animations:^{
        int index=self.pageControl.currentPage;
        self.scrollView.contentOffset=CGPointMake(320*index, 0);
    }];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    self.scrollView=nil;
    self.pageControl=nil;
    self.page0=nil;
    self.page1=nil;
    self.page2=nil;
}

@end


2、基于分页导航的实现


在iOS5之后,可以使用分页控制器(UIPageViewController)构建类似电子书效果的应用。

分页控制器需要放在一个父视图控制器中,在分页控制器下面还要有子视图控制器,每个子视图控制器对应一个页面

在基于分页导航实现的应用中,需要的类和协议有UIPageViewControllerDataSource、UIPageViewControllerDelegate和UIPageViewController,这里的UIPageViewController没有对应的视图类。


UIPageViewControllerDataSource中必须实现的方法有:

pageViewController:viewControllerBeforeViewController:,返回当前视图控制器之前的视图控制器,用于上一页的显示。

pageViewController:viewControllerAfterViewController:,返回当前视图控制器之后的视图控制器,用于下一页的显示。


UIPageViewControllerDelegate中的方法都是可选实现的,我们经常用到pageViewController:spineLocationForInterfaceOrientation:来根据屏幕旋转方向设置书脊位置和初始化首页。


在UIPageViewController中,共有两个常用的属性:双面显示(doubleSided)和书脊位置(spineLocation)。前者是指在页面翻起时偶数页面会在背面显示;单面显示在页面翻起时能够看到页面的背面,与当前内容是相反的镜像。后者是只读属性,要设置它,需要通过UIPageViewControllerDelegate中的pageViewController:spineLocationForInterfaceOrientation:方法来实现。


书脊位置由枚举UIPageViewControllerSpineLocation定义,主要有以下成员变量:

UIPageViewControllerSpineLocationMin,定义书脊位置在书的最左边(或最上面),书将从右向左翻(或从下往上翻)。

UIPageViewControllerSpineLocationMax,定义书脊位置在书的最右边(或最下面),书将从左向右翻(或从上往下翻)。

UIPageViewControllerSpineLocationMid,定义书脊位置在书的中间,一般会在横屏下显示,屏幕分成两个页面。


下面是一个简单的实现。

另外,Xcode工程模板Page-Based Application能够帮助我们快速构建分页应用。


//  ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<UIPageViewControllerDataSource,UIPageViewControllerDelegate>

@property (strong,nonatomic) UIPageViewController *pageViewController;

@end


//  ViewController.m

#import "ViewController.h"

@interface ViewController ()
{
    //记录当前页面
    int currentPage;
    UIViewController *page0,*page1,*page2;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
	UIStoryboard *storyboard=[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    page0=[storyboard instantiateViewControllerWithIdentifier:@"page0"];
    page1=[storyboard instantiateViewControllerWithIdentifier:@"page1"];
    page2=[storyboard instantiateViewControllerWithIdentifier:@"page2"];
    
    self.pageViewController=[[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    self.pageViewController.delegate=self;
    self.pageViewController.dataSource=self;
    self.pageViewController.doubleSided=NO;
    /*
     Sets the view controllers to be displayed.
     The view controllers passed to this method are those that will be visible after the animation has completed. Use a data source to provide additional view controllers to which users navigate.
     If the transition style is UIPageViewControllerTransitionStylePageCurl, the view controllers to pass in the viewControllers parameter depends on the spine location and the value of the doubleSided property:
     */
    //设置PageViewController首页
    [self.pageViewController setViewControllers:@[page0] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
    
    [self.view addSubview:self.pageViewController.view];
    currentPage=0;
    
}

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    currentPage++;
    if(currentPage>2){
        currentPage=2;
        return nil;
    }
    if(currentPage==1){
        return page1;
    }else{
        return page2;
    }
}

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    currentPage--;
    if(currentPage<0){
        currentPage=0;
        return nil;
    }
    if(currentPage==1){
        return page1;
    }else{
        return page0;
    }
}

-(UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    return UIPageViewControllerSpineLocationMin;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    page0=nil;
    page1=nil;
    page2=nil;
}

@end

上面构造UIPageViewController对象后用initWithTransitionStyle方法初始化,这里是设置页面翻转的样式。UIPageViewControllerTransitionStyle枚举定义了两个翻转样式:

UIPageViewControllerTransitionStylePageCurl,翻书效果样式。

UIPageViewControllerTransitionStyleScroll,滑屏效果样式。


navigationOrientation设置翻页方向,UIPageViewControllerNavigationOrientation枚举定义了两种翻页方式:

UIPageViewControllerNavigationOrientationHorizontal,水平方向。

UIPageViewControllerNavigationOrientationVertical,竖直方向。


方法setViewControllers:direction:animated:completion:方法用于设置首页中显示的视图。首页中显示几个视图与书脊位置有关,如果书脊在中间则首页中显示两个视图,否则首页只显示一个视图。



二、标签导航


标签导航模式是非常重要的导航模式。使用标签栏时,有一定的指导原则:标签栏位于屏幕下方,占有49点的屏幕空间,有时可以隐藏起来;为了点击方便,标签栏中的标签不能超过5个,如果超过5个,则最后一个显示为“更多”,点击“更多”标签会出现更多的列表。

在Xcode4.5中,可以使用工程模板Tabbed Application创建标签导航模式的应用,这可以通过故事板或nib技术实现。故事板创建简单,但是故事板屏蔽了许多实现细节。



三、树形结构导航


树形结构导航在iOS app中应用得也比较多,它将导航视图控制器(UINavigationController)与表视图结合使用,主要用于构建有从属关系的导航。这种导航模式采用分层组织信息的方式,可以帮助我们构建iOS效率型应用程序。在Xcode4.5中创建树形结构导航的应用,可以使用工程模板Master-Detail Application。


在树形结构导航模型中,会有两个视图控制器:一个是应用程序根视图控制器,它是UINavigationController的实例;另一个是导航控制器根视图控制器,用于提供和呈现导航控制器的一级视图,即我们看到的第一个界面。



四、iPad专用视图控制器


上面我们提到UIPopoverController和UISplitViewController是专为iPad设计的。


1、UIPopoverController


UIPopoverController是iPad特有的类,它负责控制Popover视图。Popover视图是一种临时视图,它以“漂浮”的形式出现在视图表面。触摸Popover视图的外边,则关闭视图。

由于Popover视图不会占用全屏,而且有一个箭头指向其他视图或按钮,所以Popover内容视图中也常常包含一些控件,类似表单一样。


iOS API提供了UIPopoverController和UIPopoverControllerDelegate,但没有提供与UIPopoverController对应的视图类。UIPopoverController类的常用方法和属性有:


a、setContentViewController:animated: ,设定内容视图大小。

b、presentPopoverFromRect:inView:permittedArrowDirections:animated: ,指定一个矩形区域的位置作为锚点来呈现Popover视图的方法。

c、presentPopoverFromBarButtonItem:permittedArrowDirections:animated: ,指定一个按钮作为锚点来呈现Popover视图的方法。

d、dismissPopoverAnimated: ,关闭Popover视图的方法。

e、popoverVisible,判断Popover视图是否可见。

f、popoverArrowDirection,判断Popover视图箭头的方向。



2、UISplitViewController


iPad自带的E-mail应用采用UISplitViewController,该控制器是iPad中构建导航模式应用的基础,可以呈现屏幕分栏视图的效果。由于iPad要比iPhone大很多,所以不能简单地采用iPhone的导航模式。


在横屏下,SplitView视图将屏幕分割成左右两个视图,左侧是MasterView,负责显示导航列表,用于为右侧DetailView导航;右侧是DetailView,负责显示详细信息。需要说明的是,MasterView的导航列表占有320点的固定大小。在竖屏的情况下,MasterView会隐藏起来。


有时候我们会根据需要在MaterView或DetailView中添加导航栏控制器(UINavigationController),以便在自己的视图中采用树形导航模式。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值