2014-02-08 155 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]; 
} 
相关问题