天天看點

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:

iOS導航模式
//  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),以便在自己的視圖中采用樹形導航模式。

iOS