I have a UIPageViewController
that has 3 pages. How can I switch to a page that is within the UIPageViewController
on a button tap? (Each of the pages are a separate UIViewController
that I made in the storyboard).
I've tried a lot of code but I keep getting errors as I made my UIPageViewController
inside my storyboard and I set it up like this:
PageViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = self;
self.navigationController.navigationBarHidden = NO;
[self setViewControllers:@[[self.storyboard instantiateViewControllerWithIdentifier:@"Main"]]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[NavView2ViewController class]])
return nil;
return [self.storyboard instantiateViewControllerWithIdentifier:@"two"];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[NavViewController class]])
return nil;
return [self.storyboard instantiateViewControllerWithIdentifier:@"one"];
}
You can do this a few different ways, depending on how you want to architect the rest of the app.
Using notifications will allow you to decouple the child view controllers from the parent page view controller. It will be flexible enough to allow for additional code throughout the app to control which page is visible. However one disadvantage is that there is a level of indirection and it's not immediately apparent what has triggered the change of page.
Setup an observer on your PageViewController
, something like:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = self;
self.navigationController.navigationBarHidden = NO;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleButtonTap:)
name:MyPageViewControllerButtonNotification
object:nil];
[self setViewControllers:@[[self.storyboard instantiateViewControllerWithIdentifier:@"Main"]]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
}
- (void)handleButtonTap:(NSNotification *)notification
{
// Determine which page to move to based
// on the notification object or userInfo
}
And post the notification from any of the child view controllers:
- (void)buttonTapped:(id)sender
{
// Customise the sender or userInfo in order to
// pass further information with the notification.
[[NSNotificationCenter defaultCenter] postNotificationName:MyPageViewControllerButtonNotification
object:sender
userInfo:nil];
}
You will need to create the string MyPageViewControllerButtonNotification
in a file common to both your PageViewController
and all view controllers you wish to post this notification from.
Using a delegate pattern will provide tighter coupling between view controllers and the page view controller than notifications, which may make it more difficult to extend later. The advantage of this is it's easy to track where a page change originated.
Define a protocol in a file common to your page view controller and child view controllers:
@protocol PageControlDelegate <NSObject>
- (void)moveToPage:(NSUInteger)pageNumber;
@end
Ensure your page view controller conforms to that protocol:
@interface PageViewController : UIPageViewController <PageControlDelegate>
(And implement the method in PageViewController.m
)
- (void)moveToPage:(NSUInteger)pageNumber
{
// switch to the appropriate page
}
When you are creating the view controllers for the pages that need the button interaction you will need to supply a (weak) reference to your PageViewController
:
@interface SomeChildViewController : UIViewController
@property (nonatomic, weak) id<PageControlDelegate> pageControlDelegate;
@end
And an example assignment when setting up the next page:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[NavViewController class]])
return nil;
SomeChildViewController *childController = [self.storyboard instantiateViewControllerWithIdentifier:@"one"];
childController.pageControlDelegate = self;
return childController;
}
Then you can directly invoke this method from any child view controllers that have a pageControlDelegate
:
- (void)buttonTapped:(id)sender
{
NSUInteger pageNumber = // Decide which page to move to
[self.pageControlDelegate moveToPage:pageNumber];
}