2014-02-08 152 views
33

我一直在研究一個簡單的測試應用程序,以瞭解UIPageViewController的來龍去脈。我有它的工作,但我不相信我的執行是最好的方式。我希望你們中的一些人能夠指引我正確的方向。如何實現使用多個ViewControllers的UIPageViewController

爲了獲得基本的理解,我以本教程爲出發點。 http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/

本教程將創建一個應用程序,該應用程序對UIPageViewController提供的每個頁面使用一個viewController。不過,我需要利用UIPageViewController滾動具有完全不同佈局的頁面。因此,爲了讓本教程更進一步,我創建了一個主詳圖應用程序,它在詳細視圖中使用UIPageViewController來顯示三個不同的視圖控制器。我堅持只顯示這個測試應用程序的圖像和標籤,但我目前正在構建的應用程序有三個viewController,它們將包含tableview,imageView和textViews,或一些textFields。

這是我的測試應用程序的故事板。

Storyboard

我使用DetailViewController作爲PageViewController的數據源。在DVC的viewDidLoad中,我以這種方式建立將在三個內容視圖控制器firstViewController,secondViewControllerthirdViewController中使用的標籤和圖像。

if ([[self.detailItem description] isEqualToString:@"F14's"]) { 
    //Here the page titles and images arrays are created 
    _pageTitles = @[@"Grim Reapers", @"Breakin the Barrier!", @"Top Gun"]; 
    _pageImages = @[@"F14_Grim.jpg", @"F14boom.jpg", @"F14_topgun.jpg"]; 

    //Here I call a method to instantiate the viewControllers 
    FirstController *selectedController = [self viewControllerAtIndex:0]; 
    SecondController *nextController = [self viewControllerAtIndex:1]; 
    ThirdController *lastController = [self viewControllerAtIndex:2]; 
    [_vc addObject:selectedController]; 
    [_vc addObject:nextController]; 
    [_vc addObject:lastController]; 
    _vc1 = @[selectedController]; 


} else if ([[self.detailItem description] isEqualToString:@"F35's"]){ 
    //code is above is repeated 

下面是實例化viewControllers

- (UIViewController *)viewControllerAtIndex:(NSUInteger)index 
{ 
    if (([self.pageTitles count] == 0) || (index >= [self.pageTitles count])) { 
     return nil; 
    } 

    // Create a new view controller and pass suitable data. 
    if (index == 0) { 
     FirstController *fvc = [self.storyboard instantiateViewControllerWithIdentifier:@"FirstPageController"]; 
     fvc.imageFile = self.pageImages[index]; 
     fvc.titleText = self.pageTitles[index]; 
     fvc.pageIndex = index; 
     if ([_vc count]) { 
      //Here I have to replace the viewController each time it is recreated 
      [_vc replaceObjectAtIndex:0 withObject:fvc]; 
     } 
     return fvc; 
    } else if (index == 1) { 
//Code is repeated for remaining viewControllers 

viewDidLoad的代碼是一個領域,我覺得我做unnecassary工作方法。我不相信我需要在加載DVC時實例​​化所有三個視圖控制器,但我不知道如何爲UIPageViewControllerDataSource協議方法(viewControllerBeforeViewControllerviewControllerAfterViewController)提供數組。

這裏是​​方法。

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController 
{ 

    NSUInteger index = [_vc indexOfObject:viewController]; 

    if ((index == 0) || (index == NSNotFound)) { 
     return nil; 
    } 

    index--; 

    //notice here I call my instantiation method again essentially duplicating work I have already done! 
    return [self viewControllerAtIndex:index]; 
} 

總之,似乎我unnecassarily與從一個網頁到另一個每次刷卡重新創建視圖控制器。這僅僅是pageViewController的工作原理,或者讓我的方式過程複雜化。任何輸入都會很棒!

SOLUTION

馬特使用標識符建議了一種非常簡單的解決方案。在我的故事板,我只是檢查了使用我已經實現故事板標識符作爲恢復標識符

RestorationId

然後在viewDidLoad而不是創造viewControllers陣列盒,只需創建與恢復標識符字符串數組。

if ([[self.detailItem description] isEqualToString:@"F14's"]) { 
    _pageTitles = @[@"Grim Reapers", @"Breakin the Barrier!", @"Top Gun"]; 
    _pageImages = @[@"F14_Grim.jpg", @"F14boom.jpg", @"F14_topgun.jpg"]; 
    FirstController *selectedController = [self viewControllerAtIndex:0]; 
    [_vc addObject:@"FirstPageController"]; 
    [_vc addObject:@"SecondPageController"]; 
    [_vc addObject:@"ThirdPageController"]; 
    _vc1 = @[selectedController]; 

最後確定委託方法的指數做到這一點,而不是之前,我在做什麼:

NSString * ident = viewController.restorationIdentifier; 
NSUInteger index = [_vc indexOfObject:ident]; 

現在的工作,而無需不必要的實例化視圖控制器。

作爲最後一個音符,如果有人正在使用的正是我在這裏,你可以擺脫從viewControllerAtIndex:方法下面的代碼片斷的。

if ([_vc count]) { 
    //Here I have to replace the viewController each time it is recreated 
    [_vc replaceObjectAtIndex:0 withObject:fvc]; 
} 
+0

你能上傳一些關於這個例子的代碼嗎?謝謝! – fguespe

+0

@fguespe你應該有這裏所需的一切來解決這個問題......不知道你在找什麼......注意我在我的問題的底部添加了一個解決方案 – Ben

+0

這是一個很棒的解決方案。但是我無法獲得用於其他功能的正確頁面索引。您是否將NSString * ident等放入根控制器或模型的 - (NSUInteger)indexOfViewController:(embBaseViewController *)viewController中? – malaki1974

回答

37

首先,你是絕對正確的,構成了UIPageViewController的「頁面」視圖控制器在本質上可以完全不同。沒有什麼說他們必須是相同的視圖控制器類的實例。

現在讓我們實際的問題,這是你非常明智地需要一種方式來提供給當前視圖控制器中的一個或下一個視圖控制器。這確實是使用頁面視圖控制器時的主要問題。

拿着視圖控制器的數組並不是真的太糟糕了。畢竟,視圖控制器是一個輕量級對象(它是視圖是重量級對象)。然而,你處理這件事的方式似乎很笨拙。

我的建議是:如果您打算將視圖控制器實例保存在故事板中,那麼爲什麼不保留一組標識符?現在你已經有了三個字符串的數組。你有多簡單?您還需要一個實例變量,用於跟蹤哪個標識符對應於視圖控制器,該視圖控制器將其視圖用作當前的頁面(以便您可以計算出哪一個是「下一個」或「上一個」);這可能只是一個整數索引到數組中。

沒有那麼絕對沒有錯,每個用戶的「翻頁」的時候實例化一個視圖控制器。這就是你需要假設需要做視圖控制器。你可以很容易地通過標識符做到這一點。

最後,請注意,如果您使用頁面視圖控制器的滾動樣式,則甚至不必這樣做,因爲頁面視圖控制器會緩存視圖控制器並停止調用委託方法(或者至少,稱他們更少)。

+0

馬特,你的建議太棒了!令人尷尬的是,花了我幾個小時的時間才弄清楚如何解決問題。這是因爲我沒有辦法使用StoryBoard標識符來確定當前使用的視圖控制器。直到我即將放棄,我偶然發現了修復標識符。一旦我發現那顆小寶石,它就是在公園散步。感謝您的意見。我覺得我的新執行不那麼「笨拙」。 – Ben

+0

恢復標識符的想法可能比我的想法更優雅,它只是爲了跟蹤標識符數組中的當前索引。 – matt

+0

馬特的好回答。 –

8

過這個問題就來了,因爲我一直在尋找一個解決這個同樣的問題 - 感謝馬特爲他提供和奔的解決方案描述的指導。

我建了一個示例項目,瞭解這一點我自己,因爲我注意到一些評論,要求樣本代碼我已經上傳這GitHub上。我的解決辦法模仿馬特的建議的方法& Ben的規定由解決方案:

  • 在故事板的每個視圖控制器屬於PageViewController的一部分設置恢復的ID。
  • 使用包含上述RestorationID的NSString對象的NSArray。
  • 基於此配置實例化適當的視圖控制器。

此外,執行問題,我試圖解決需要從子視圖控制器導航向後/向前的能力,所以這個示例項目也支持該功能通過讓根視圖控制器去上一個或下一頁(這也可以應用於轉到特定頁面)。

Sample code on GitHub

邊注:我承認希望類似的UITabBarController,我從故事板中,並指定視圖控制器的順序,但可惜只是線的一切行動似乎也並不像我們」還沒有(截至Xcode 6/iOS 8.1)。然而,該解決方案所需的代碼非常簡單直接。

1

基本上,我設法得到它是基於由的XCode 6.4(基於頁面的應用程序)的方法,並從其他作者的見解提供的模板稍微不同的方式(包括this,@Ben從其他答案):

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    self.viewControllersArray = @[@"FirstViewController", @"SecondViewController"]; 
... 
} 

... 

- (UIViewController *)viewControllerAtIndex:(NSUInteger)index { 

    UIViewController *childViewController = [self.storyboard instantiateViewControllerWithIdentifier:[self.viewControllersArray objectAtIndex:index]]; 
    //childViewController.index = index; 
    return childViewController; 

} 

- (NSUInteger)indexOfViewController:(UIViewController *)viewController { 
    NSString *restorationId = viewController.restorationIdentifier; 
    return [self.viewControllersArray indexOfObject:restorationId]; 
} 

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { 

    NSUInteger index = [self indexOfViewController:(UIViewController *)viewController]; 
    if ((index == 0) || (index == NSNotFound)) { 
     return nil; 
    } 

    index--; 
    return [self viewControllerAtIndex:index]; 
} 

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { 

    NSUInteger index = [self indexOfViewController:(UIViewController *)viewController]; 
    if (index == NSNotFound) { 
     return nil; 
    } 

    index++; 
    if (index == [self.viewControllersArray count]) { 
     return nil; 
    } 
    return [self viewControllerAtIndex:index]; 
} 
0
@interface ViewController() 
{ 
    NSMutableArray * StoryboardIds; 
} 
@end 

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 
    StoryboardIds = [[NSMutableArray alloc]init]; 
    [StoryboardIds addObject:@"vc1"]; 
    [StoryboardIds addObject:@"vc2"]; 

    UIViewController *selectedController = [self viewControllerAtIndex:0]; 

    self.ProfilePageViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageControl"]; 
    self.ProfilePageViewController.dataSource = self; 

    _viewcontrollers = [NSMutableArray new]; 

    [_viewcontrollers addObject:selectedController]; 

    [self.ProfilePageViewController setViewControllers:_viewcontrollers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; 

    // Change the size of page view controller 
    self.ProfilePageViewController.view.frame = CGRectMake(0, 0, self.bodypage.frame.size.width, self.bodypage.frame.size.height); 

    [self addChildViewController:self.ProfilePageViewController]; 
    [self.ProfilePageViewController.view setFrame:self.bodypage.bounds]; 
    [self.bodypage addSubview:self.ProfilePageViewController.view]; 
    [self.ProfilePageViewController didMoveToParentViewController:self]; 

} 
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index 
{ 
    if (([StoryboardIds count] == 0) || (index >= [StoryboardIds count])) { 
     return nil; 
    } 

    if (index == 0) { 
     vc1 *fvc = [self.storyboard instantiateViewControllerWithIdentifier:@"vc1"]; 
     fvc.Pageindex = index; 
     if ([_viewcontrollers count]) { 
      [_viewcontrollers replaceObjectAtIndex:0 withObject:fvc]; 
     } 
     return fvc; 
    } 
    else 
    { 
     vc2 *fvc = [self.storyboard instantiateViewControllerWithIdentifier:@"vc2"]; 
     fvc.Pageindex = index; 
     if ([_viewcontrollers count]) { 
      [_viewcontrollers replaceObjectAtIndex:0 withObject:fvc]; 
     } 
     return fvc; 
    } 

} 

-(NSUInteger)indexofViewController 
{ 
    UIViewController *currentView = [self.ProfilePageViewController.viewControllers objectAtIndex:0]; 

    if ([currentView isKindOfClass:[vc2 class]]) { 
     return 1; 
    } 
    else{ 
     return 0; 
    } 

} 


- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { 

    NSUInteger index = [self indexofViewController]; 

    if ((index == 0) || (index == NSNotFound)) { 
     return nil; 
    } 

    index--; 
    return [self viewControllerAtIndex:index]; 
} 

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { 

    NSUInteger index = [self indexofViewController]; 

    if (index == NSNotFound) { 
     return nil; 
    } 

    index++; 

    if (index == [StoryboardIds count]) { 
     return nil; 
    } 
    return [self viewControllerAtIndex:index]; 
} 
相關問題