5

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.

Community
  • 1
  • 1
theDC
  • 6,364
  • 10
  • 56
  • 98
  • I have an answer in that link, but unfortunately my answer don't have any upvotes, but using delegate is the only way(for me at least) to track the index correctly. Do let me know if you want to look at my implementation(ObjC)... – 0yeoj Apr 26 '16 at 07:34
  • i think @0yeoj solution is what you are looking for: http://stackoverflow.com/a/33818615/2301271 – Joshua Apr 26 '16 at 07:40
  • @0yeoj feel free to post your implementation here, preferably in swift – theDC Apr 26 '16 at 08:31

2 Answers2

3

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!

theDC
  • 6,364
  • 10
  • 56
  • 98
  • Hello DCDC, where do you place the "yourPageContentViewControllerInstance.delegate = self". Do you loop over all the VControllers and assign the delegate = parent? – drv Feb 09 '18 at 23:34
3

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
0yeoj
  • 4,500
  • 3
  • 23
  • 41