简体   繁体   中英

Changing UIPageViewController's page programmatically doesn't update the UIPageControl

In my custom UIPageViewController class:

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.model = [[BSTCMWelcomingPageViewModel alloc] init];
        self.dataSource = self.model;
        self.delegate = self;

        self.pageControl = [UIPageControl appearance];
        self.pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
        self.pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
    }
    return self;
}

Then I programmatically set the current ViewController when a button is hit:

- (void)scrollToNext
{
    UIViewController *current = self.viewControllers[0];
    NSInteger currentIndex = [self.model indexForViewController:current];
    UIViewController *nextController = [self.model viewControllerForIndex:++currentIndex];
    if (nextController) {
        NSArray *viewControllers = @[nextController];
        // This changes the View Controller, but PageControl doesn't update
        [self setViewControllers:viewControllers
                       direction:UIPageViewControllerNavigationDirectionForward
                        animated:YES
                      completion:nil];
        //Nothing happens!
        [self.pageControl setCurrentPage:currentIndex];

        //Error: _installAppearanceSwizzlesForSetter: Not a setter!
        [self.pageControl updateCurrentPageDisplay];
    }
}

If I can't do this with the UIPageControl that "belongs" to my UIPageViewController I will just try to make my own. But it would be nice if this was possible tho!

to update your UIPageControl indicator, you need to implement one data source method of UIPageViewController (the UIPageViewControllerDataSource method) :

-(NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController

This method is responsible for updating the page control indicator (when you use UIPageViewController). You only need to return the currentpage value in this method. The Method gets called by default when you use/make a call for setViewControllers on your custom UIPageViewController.

So the chunk of code that you need to write is:

- (void)scrollToNext
{
    UIViewController *current = self.viewControllers[0];
    NSInteger currentIndex = [self.model indexForViewController:current];
    UIViewController *nextController = [self.model viewControllerForIndex:++currentIndex];
    if (nextController) {
        NSArray *viewControllers = @[nextController];
       // This changes the View Controller and calls the presentationIndexForPageViewController datasource method
       [self setViewControllers:viewControllers
                   direction:UIPageViewControllerNavigationDirectionForward
                    animated:YES
                  completion:nil];
}

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
    return currentIndex;
}

Hope this solves your problem. :)

As mentioned in accepted answer, you need to implement

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController

But for me it was enough to use it like this:

Objective-C:

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
    // The selected item reflected in the page indicator.
    return [self.controllers indexOfObject:[pageViewController.viewControllers firstObject]];
}

Swift 3+:

func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        guard let index = viewControllers?.index(of: (pageViewController.viewControllers?.first)!) else { return 0 }
        return index
}

Without the need to remember the current index. Where self.controllers is an NSArray of UIViewControllers displayed in given UIPageViewController . I'm not sure how exactly your BSTCMWelcomingPageViewModel works, but it should be easy to adjust.

Xamarin/C# Solution

I had this problem in Xamarin, here is my version of @micromanc3r's solution:

public class PageViewControllerDataSource : UIPageViewControllerDataSource
{
    UIViewController[] pages;

    public PageViewControllerDataSource(UIViewController[] pages)
    {
        this.pages = pages;
    }

...

    public override nint GetPresentationIndex(UIPageViewController pageViewController)
    {
        return Array.IndexOf(pages, pageViewController.ViewControllers[0]);
    }
}

A page indicator will be visible if both methods are implemented, transition style is UIPageViewControllerTransitionStyleScroll and navigation orientation is UIPageViewControllerNavigationOrientationHorizontal . Both methods are called in response to a setViewControllers:... call, but the presentation index is updated automatically in the case of gesture-driven navigation.

  • (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController NS_AVAILABLE_IOS(6_0); : The number of items reflected in the page indicator.

  • (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController NS_AVAILABLE_IOS(6_0); : The selected item reflected in the page indicator.

The answer above, given by aansul, works.

Note : Don't forget to set the pageViewController's Transition style to Scroll instead of Page Curl. Otherwise it won't work.

// ViewController.h File

#import <UIKit/UIKit.h>
#import "PageContentViewController.h"

@interface ViewScreen : UIViewController<UIPageViewControllerDataSource,UIPageViewControllerDelegate>

- (IBAction)Startwalkthrough:(id)sender;

@property(strong, nonatomic)UIPageViewController *pageViewController;
@property(strong, nonatomic)NSArray * pageTitles;
@property(strong,nonatomic)NSArray * pageImages;

@end

// ViewController.m File
#import "ViewScreen.h"

@interface ViewScreen ()

@end

@implementation ViewScreen

@synthesize pageTitles,pageImages;

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    pageTitles =@[@"dianna",@"images",@"image",@"i1",@"hulk1",@"assasins"];
    pageImages =@[@"dianna",@"images",@"image",@"i1",@"hulk1",@"assasins"];

    self.pageViewController =[self.storyboard instantiateViewControllerWithIdentifier:@"PageViewController"];
    self.pageViewController.dataSource=self;

    PageContentViewController *startingViewController = [self viewControllerAtIndex:0];

    NSArray * viewController =@[startingViewController];

    [self.pageViewController setViewControllers:viewController direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

    [self addChildViewController:_pageViewController];

    [self.view addSubview:_pageViewController.view];
    [self.pageViewController didMoveToParentViewController:self];


}

-(IBAction)Startwalkthrough:(id)sender
{
    PageContentViewController * startingViewController =[self viewControllerAtIndex:0];
    NSArray * viewControllers =@[startingViewController];
    [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];

}

- (PageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
    if (([self.pageTitles count] == 0) || (index >= [self.pageTitles count])) {
        return nil;
    }

    // Create a new view controller and pass suitable data.
    PageContentViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageContentViewController"];
    pageContentViewController.imageFile = self.pageImages[index];
    pageContentViewController.titletext = self.pageTitles[index];
    pageContentViewController.pageIndex = index;

    return pageContentViewController;
}


#pragma mark - Page View Controller Data Source

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger index =((PageContentViewController *) viewController).pageIndex;
    if (index == NSNotFound) {
        return nil;
    }

    index--;
    if (index ==[self.pageTitles count]) {
        return  nil;

}
    return [self viewControllerAtIndex:index];

}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = ((PageContentViewController*) viewController).pageIndex;

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

    index++;
    if (index == [self.pageTitles count]) {
        return nil;
    }
    return [self viewControllerAtIndex:index];
}

-(NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController{
    return  [self.pageTitles count];
}

-(NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
    return  0;
}
@end


// Second File

PageViewController.h File 
#import <UIKit/UIKit.h>

@interface PageContentViewController : UIViewController

@property (strong, nonatomic) IBOutlet UILabel *txtLabel;
@property (strong, nonatomic) IBOutlet UIImageView *backgroundImageView;

@property NSUInteger pageIndex;
@property  NSString *titletext;
@property NSString * imageFile;
@end


// SecondFile.M File
#import "PageContentViewController.h"

@interface PageContentViewController ()

@end

@implementation PageContentViewController



-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self =[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        //
    }
    return  self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    self.backgroundImageView.image =[UIImage imageNamed:self.imageFile];
    self.txtLabel.text =self.titletext;

}

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


/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

确保在调用setViewControllers之前设置currentIndex

SWIFT 4.2

func presentationIndex(for pageViewController: UIPageViewController) -> Int {
    guard let currentController = pageViewController.viewControllers?.first else { 
     return 0 }
    guard let index = viewControllerList.index(of: currentController) else { return 0 }
    return index
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM