Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS - UIPageViewControllerDataSource method called twice

I found a weird issue with the two UIPageViewControllerDataSource methods: the first time I enter the page view controller scene and drag on the content of the controller, no matter what the dragging direction is, both of the data source methods get called. I suppose that when I'm at the first page and drag to the right, which means that there is no more page before the first page, neither of the methods should get called. And if I drag to the left, only the after method should get called.

I followed this post to set up the view controllers (except that I do not have a separate page view controller on the storyboard. I used the traditional [UIPAgeViewController alloc] init] method to instantiate the controller). Below is my code:

For the view controller that actually displays the content of the page view controller (only the relevant part of viewDidLoad is shown):

 - (void)viewDidLoad 
{
    // initialize page view controller
    _pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    self.pageViewController.delegate = self;
    self.pageViewController.dataSource = self;

    // set up the initial scene of the page view controller
    UIViewController *viewController = [self viewControllerAtIndex:0];
    [self.pageViewController setViewControllers:@[viewController]
                                      direction:UIPageViewControllerNavigationDirectionForward
                                       animated:NO
                                     completion:nil];

    // adjust the size of the page view controller, -44 to show the buttons at the bottom
    self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 44);

    // display the content of the page view controller
    [self addChildViewController:self.pageViewController];
    [self.view addSubview:self.pageViewController.view];
    [self.pageViewController didMoveToParentViewController:self];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
      viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger index = ((PWCPageContentViewController *)viewController).index;
    if (index == 0 || index == NSNotFound) {
        return nil;
    }
    --index;
    return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
       viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = ((PWCPageContentViewController *)viewController).index;
    if (index == self.numberOfPages - 1 || index == NSNotFound) {
        return nil;
    }
    ++index;
    return [self viewControllerAtIndex:index];
}

- (PWCPageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
    PWCPageContentViewController *viewController =
    [self.storyboard instantiateViewControllerWithIdentifier:@"PageContentViewController"];
    viewController.index = index;
    NSString *text = [self.notes getNoteAtIndex:index];
    [viewController.textView setText:text];

    return viewController;
}

For PWCPageContentViewController.h:

#import <UIKit/UIKit.h>

@class PWCNotes;

@interface PWCPageContentViewController : UIViewController <UITextViewDelegate>

@property NSUInteger index;
@property (weak, nonatomic) IBOutlet UITextView *textView;

- (void)dismissKeyboard;

@end

For PWCPageContentViewController.m:

#import "PWCPageContentViewController.h"

@interface PWCPageContentViewController ()

@end

@implementation PWCPageContentViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.textView.delegate = self;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)dismissKeyboard
{
    if ([self.textView isFirstResponder]) {
        // dismiss the keyboard when hitting save
        [self.textView resignFirstResponder];
    }
}

@end

So is this a known issue or am I missing something?

like image 761
ljiatu Avatar asked Apr 08 '14 01:04

ljiatu


3 Answers

Solution to this problem in Swift:

I encountered the bug when I used more than 3 UIPageViewController (try with 4 UIPageViewController for example, using the @Rashad's snippet). The problem came from a missing and non implemented optional UIPageViewControllerDelegate's method, which drove the UIPageControl crazy and unstable.

Please find the particular method which fixes the bug below:

/*
    Optional UIPageViewControllerDelegate method

    Sent when a gesture-initiated transition ends. The 'finished' parameter indicates whether
    the animation finished, while the 'completed' parameter indicates whether the transition completed or bailed out (if the user let go early).
*/
func pageViewController(pageViewController: UIPageViewController,
    didFinishAnimating finished: Bool,
    previousViewControllers: [AnyObject],
    transitionCompleted completed: Bool)
{
    // Turn is either finished or aborted
    if (completed && finished) {
        let currentDisplayedViewController = self.pageViewController!.viewControllers[0] as! ContentViewController
        self.pageControl.currentPage = currentDisplayedViewController.index
    }
}

If you want to get the full working project, please find the Github link below:

(Github link of the project)

like image 156
King-Wizard Avatar answered Sep 24 '22 20:09

King-Wizard


UIPageViewController uses these bellow method for getting the back and front ViewController.

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController;
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController;

Though its weird but yes its a known issue. Every time it calls Before and After method to get VC. If there is no next VC then it returns nil and if there is no previous VC the datasourceDelegate return nil, otherwise it return the index of VC.

In UIPageViewControllerDelegate, there is a function named :

 - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers;

Look at this it might help to get the current or next/previous viewcontroller from pendingViewControllers array.

Hope this helps.. :)

like image 30
Rashad Avatar answered Sep 24 '22 20:09

Rashad


So as @Rashad suggested, pageViewController:willTransitionToViewControllers: isn't messed up. It is called exactly once when you transition to the previous/next view controller (in case that the previous/next view controller is not nil). This also applies to pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:, which is called exactly once when the animation finishes.

So if you want to do any index-based things and you want to get the correct index for the upcoming view controllers, or you want to get the correct view controller, consider using these two methods as a workaround for now.

like image 23
ljiatu Avatar answered Sep 24 '22 20:09

ljiatu