Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A reliable way to get UIPageViewController current index

I'm looking for a reliable way to keep track of UIPageViewController current index.

The problem is well known; Although viewControllers are being presented properly, it hard to keep track of the current index.

I thought it will be good to refresh this topic across SO community since it remains unsolved for some reason

I've browsed through many threads here, but most answers are outdated or marked as unreliable(The result depends on if user did a full swipe or just half swiped etc)

I've visited this thread, but it doesn't provide any explicitly correct answer.

I've tried:

1) Keeping track of viewController's view tag - link - always returns 0

2) Looking at index variable in both UIPageViewController methods, viewControllerBeforeViewController and viewControllerAfterViewControlle

its results is unpredictable, sometimes it skips over one index etc.

Have anybody come up with a good way, reliable way to keep track of UIPageCiewController index to make use of it(for example print current index)?

I'd appreciate both obj-c and swift implementation, but swift is the one I'm looking for.

like image 924
theDC Avatar asked Apr 26 '16 06:04

theDC


2 Answers

This is for ObjC

ParentViewController

#import "PagesViewController.h"

// Delegate: PageViewDelegate
// Declared inside `PagesViewController`
//
@interface ParentViewController () <UIPageViewControllerDataSource, UIPageViewControllerDelegate, PageViewDelegate>

@property (nonatomic) UIPageViewController *pageViewController;

@end

@implementation ViewController

- (void)viewDidLoad 
{
    [super viewDidLoad];

    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    self.pageViewController.dataSource = self;
    self.pageViewController.view.frame = self.view.frame;

    // im setting page 3 as the default page
    //
    [self.pageViewController setViewControllers:[NSArray arrayWithObject:[self viewControllerAtIndex:3]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
    [self addChildViewController:self.pageViewController];
    [self.view addSubview:self.pageViewController.view];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController 
{
    NSUInteger index = [(PagesViewController *)viewController index];

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

    index--;

    return [self viewControllerAtIndex:index];
}

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

    NSUInteger index = [(PagesViewController *)viewController index];

    index++;

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

    return [self viewControllerAtIndex:index];
}

- (PagesViewController *)viewControllerAtIndex:(NSInteger)index
{
    PagesViewController *vc = [[PagesViewController alloc] init];

    // set delegate here..
    //
    vc.delegate = self;

    // other data
    //
    vc.index = index;
    vc.titleLabel = [NSString stringWithFormat:@"Screen :%ld", (long)index];

    return vc;
}

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController 
{ // The number of items reflected in the page indicator. return x; }

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{ // The selected item reflected in the page indicator. return x; }

// This is what you need
//
- (void)viewController:(id)VC didShowWithIndex:(long)index
{
    NSLog(@"didShowWithIndex: %ld", index);
}

PagesViewController.h

@protocol PageViewDelegate <NSObject>

- (void)viewController:(id)VC didShowWithIndex:(long)index;

@end


@interface PagesViewController : UIViewController

@property (weak) id <PageViewDelegate> delegate;

@property (nonatomic) NSInteger index;

@property (nonatomic) NSString *titleLabel;

@end

PagesViewController.m

@implementation PagesViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor blackColor];

    UILabel *titleLabel = [[UILabel alloc] initWithFrame:self.view.frame];
    titleLabel.textAlignment = NSTextAlignmentCenter;
    titleLabel.textColor = [UIColor whiteColor];
    titleLabel.text = self.titleLabel;
    [self.view addSubview:titleLabel];
}

// Trigger delegate here
//
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [self.delegate viewController:self didShowWithIndex:self.index];
}

@end
like image 187
0yeoj Avatar answered Oct 20 '22 18:10

0yeoj


Like @0yeoj suggested, delegation design patter is the solution here. It requires a bit of thinking outside the box.

Let's add protocol IndexDelegate and modify a bit PageContentViewController

protocol IndexDelegate {
func showIndex(index:Int)
 }

import UIKit

class PageContentViewController: UIViewController {

@IBOutlet weak var imageView: UIImageView!
var delegate:IndexDelegate?
var pageIndex:Int = 0
var imageFile:String!
override func viewDidLoad() {
    super.viewDidLoad()
    self.imageView.image = UIImage(named: self.imageFile)
    }
override func viewDidAppear(animated: Bool) {
    self.delegate!.showIndex(self.pageIndex)
    }

now in our parent view controller we conform to protocol

func showIndex(index: Int) {
  print(index)
  }

don't forget to set yourPageContentViewControllerInstance.delegate = self and inherit protocol YourParentViewController:UIViewController, UIPageViewControllerDataSourceDelegate, IndexDelegate {}

That's it! Works perfectly and reliably and doesn't lag at all!

like image 39
theDC Avatar answered Oct 20 '22 18:10

theDC