iOS - UIPageViewControllerDataSource method called twice

元气小坏坏 提交于 2019-12-22 04:18:06

问题


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?


回答1:


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.. :)




回答2:


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)




回答3:


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.




回答4:


change the transition style from scroll to pageCurl

pageViewController = UIPageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)

it's look better and it will not called twice



来源:https://stackoverflow.com/questions/22926226/ios-uipageviewcontrollerdatasource-method-called-twice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!