UIPageViewController: return the current visible view
As of iOS 6 I've found that the viewControllers
property of UIPageViewController constantly updates so that it will always hold the one view controller that represents the current page, and nothing else. Thus, you can access the current page by calling viewControllers[0]
(Assuming you only show one view controller at a time).
The viewController array only updates once the page "locks" into place, so if a user decides to partially reveal the next page it doesn't become the "current" page unless they complete the transition.
If you want to keep track of the "page numbers" assign your view controllers an index value as you create them through the UIPageViewController datasource methods.
So for example:
-(void)autoAdvance
{
UIViewController *currentVC = self.viewControllers[0];
NSUInteger currentIndex = [myViewControllers indexOfObject:currentVC];
if ( currentIndex >= (myViewControllers.count-1) ) return;
[self setViewControllers:@[myViewControllers[ currentIndex+1 ]]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
}
-(NSInteger)presentationIndexForPageViewController:
(UIPageViewController *)pageViewController
{
// return 0;
UIViewController *currentVC = self.viewControllers[0];
NSUInteger currentIndex = [myViewControllers indexOfObject:currentVC];
return currentIndex;
}
But note the comments that this is unreliable.
Building on Ole's Answer…
This is how I implemented the 4 methods to track the current page and update the page indicator to the correct index:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController{
return (NSInteger)[self.model count];
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController{
return (NSInteger)self.currentIndex;
}
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers{
SJJeanViewController* controller = [pendingViewControllers firstObject];
self.nextIndex = [self indexOfViewController:controller];
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{
if(completed){
self.currentIndex = self.nextIndex;
}
self.nextIndex = 0;
}
You should manually keep track of the current page.
The delegate method pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
will tell you when to update that variable. The last argument of the method transitionCompleted:
can tell you whether a user completed a page turn transition or not.
Then, you can get the currently presented View Controller by doing
self.viewControllers?.first
Unfortunately, all above methods didn't help me. Nevertheless, I have found the solution by using tags. May be it's not the best, but it works and hope it helps someone:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
if (completed) {
int currentIndex = ((UIViewController *)self.pageViewController.viewControllers.firstObject).view.tag;
self.pageControl.currentPage = currentIndex;
}
}
In Swift: (thanks to @Jessy)
func pageViewController(pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool)
{
guard completed else { return }
self.pageControl.currentPage = pageViewController.viewControllers!.first!.view.tag
}
Example: gist