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

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]);
}
}

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
}

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

Make sure to set the currentIndex BEFORE you call setViewControllers

Related

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.
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 #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!

UIPageViewController index is not working properly

Basically, I am making a page app, which use UIPageViewController. When the user scrolls the page, they can see different content. The problem now is, when I scroll the page, the first and second page works properly, and the index is correct. But when I scroll to the third page, the index keeps at "1", and don't change afterwards. And the other page keeps the appearance of the second page, whose index is "1".
I was following the tutorial: http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/
My code:
View Controller.h
#import <UIKit/UIKit.h>
#import "PageContentViewController.h"
#import "PageViewController.h"
#interface ViewController : UIViewController<UIPageViewControllerDataSource, UIPageViewControllerDelegate>
#property (strong, nonatomic) PageViewController *pageViewController;
#property (strong, nonatomic) NSArray *pageTitles;
#end
View Controller.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.pageTitles = #[#"First Page", #"Second Page", #"Third Page"];
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
if ([self.pageTitles count])
{
PageContentViewController *startingViewController = [self viewControllerAtIndex: 0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
}
- (PageContentViewController *) viewControllerAtIndex: (NSUInteger)index{
if (index < [self.pageTitles count])
{
PageContentViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageContentViewController"];
pageContentViewController.titleText = self.pageTitles[index];
NSLog(#"%li", index);
UIColor *backgroundColor;
switch (index) {
case 0:
backgroundColor = [UIColor greenColor];
break;
case 1:
backgroundColor = [UIColor blueColor];
break;
case 2:
backgroundColor = [UIColor purpleColor];
break;
default:
break;
}
pageContentViewController.backgroundColor = backgroundColor;
return pageContentViewController;
}
return nil;
}
- (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];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController *)viewController).pageIndex;
if (index == 0 || index == NSNotFound)
{
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (NSInteger) presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return [self.pageTitles count];
}
- (NSInteger) presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Page Content View Controller.h
#import <UIKit/UIKit.h>
#interface PageContentViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIView *backgroundView;
#property (weak, nonatomic) IBOutlet UILabel *titleLable;
#property (nonatomic) NSUInteger pageIndex;
#property (strong, nonatomic) NSString *titleText;
#property (strong, nonatomic) UIColor *backgroundColor;
#end
Page Content View Controller.m
#import "PageContentViewController.h"
#interface PageContentViewController ()
#end
#implementation PageContentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.titleLable.text = self.titleText;
[self.backgroundView setBackgroundColor:self.backgroundColor];
}
- (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
In viewControllerAtIndex:, you need to set the value of pageIndex property of the pageContentViewController:
pageContentViewController.pageIndex = index;

Why isn't my UILabel being set?

I have 2 view controllers. viewController1 has a UILabel called nameLabel which I want to set in a different class, viewController2.
I try calling this code from viewController2.
Content1ViewController *viewController1 = [Content1ViewController new];
viewController1.nameLabel.text = #"HELLO";
NSLog(#"%#",viewController1.nameLabel);
However, the viewController1 nameLabel doesn't change when I call the code? Also the NSLog returns "null"?? Can someone tell me why this is happening and also how I can change the nameLabel from a different class? Thanks!
Controls are not initialized when you manually create the instance. This is done on a later stage of view controller life cycle.
If I'm not mistaken, first event where you will see controls initialized is viewDidLoad
Something you can do is adding a NSString property named (let's say) nameLabelText and do nameLabel.text = nameLabelText; on viewDidLoad
You need to take the same instance of viewController which was on the window. If you are pushing viewController2 into navigation controller stack then get instance of viewController1 from navigation stack and then try to set the label.
If you are using present modal then use presentingViewController instance of viewController2 in order to access instance of viewController1 like
viewController1 *controller = [viewController2 presentingViewController];
[controller.nameLabel setText:#"WhatEver"];
Where have you set the property for namelabel?
Did you use Interface Builder (XIB or Storyboard) or have you done it all in code?
If the you did it in InterfaceBuilder than
[Content1ViewController new]
is the wrong approach.
For XIB-Files you should use
initWithNibName:(NSString *)nibName
bundle:(NSBundle *)nibBundle
and if your ViewController has been defined in a storyboard you should use
instantiateViewControllerWithIdentifier:(NSString *)identifier
from UIStoryboard.
But as Claudio Redi already said the earliest point where you will be able to access the UILabel will be in viewDidLoad
If everything in code, you should put
[self setNameLabel:[[UILabel alloc] initWithFrame:CGRectMake(50.0f,50.0f,100.0f,100.0f)];
in the init-Method of your ViewController.
//
// ViewController.m
// UIPageViewControllerDemo
//
// Created by YunYi1118 on 15/5/20.
// Copyright (c) 2015年 Xiaoyi. All rights reserved.
//
#import "ViewController.h"
#import "MoreViewController.h"
#interface ViewController () <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
#property (strong, nonatomic) UIPageViewController *pageController;
#property (strong, nonatomic) NSArray *pageContent;
#end
#implementation ViewController
#synthesize pageContent = _pageContent;
#synthesize pageController = _pageController;
- (void)viewDidLoad {
[super viewDidLoad];
[self createContentPages];
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin] forKey:UIPageViewControllerOptionSpineLocationKey];
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:options];
_pageController.dataSource = self;
_pageController.view.frame = self.view.bounds;
MoreViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:_pageController];
[self.view addSubview:_pageController.view];
}
- (MoreViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (self.pageContent.count == 0 || index >= self.pageContent.count) {
return nil;
}
MoreViewController *dataViewController = [[MoreViewController alloc] init];
dataViewController.dataObject = [self.pageContent objectAtIndex:index];
return dataViewController;
}
- (void)createContentPages
{
NSMutableArray *pageStrings = [NSMutableArray array];
for (int i = 1; i < 11; i++) {
NSString *contentString = [[NSString alloc] initWithFormat:#"Chapter%d, This is the page %d of content displayed using UIPageViewController ",i,i];
[pageStrings addObject:contentString];
}
self.pageContent = [NSArray arrayWithArray:pageStrings];
}
#pragma mark - delegate
- (NSUInteger)indexOfViewController:(MoreViewController *)viewController
{
return [self.pageContent indexOfObject:viewController.dataObject];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(MoreViewController *)viewController];
if (index == 0 || index == NSNotFound) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(MoreViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == self.pageContent.count) {
return nil;
}
return [self viewControllerAtIndex:index];
}
#end

How can I dismiss/pop a UIPageViewController after the last view controller is swiped?

How can I dismiss/pop a UIPageViewController after the last view controller is swiped?
Basically want to make a tutorial style paging view with images, and to dismiss after the user swipes to the "next" page from the last.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [(EmailTutorialViewController *)viewController index];
index++;
if (index == 5) {
[self.navigationController popViewControllerAnimated:YES]; // this doesn't do anything
return nil;
}
return [self viewControllerAtIndex:index];
}
Here's simple solution I've used:
In
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController;
return empty controller for index == 5 and add this UIPageViewControllerDelegate method:
- (void)pageViewController:(UIPageViewController *)pageViewController
willTransitionToViewControllers:(NSArray *)pendingViewControllers
{
EmailTutorialViewController *viewController =
[pendingViewControllers objectAtIndex:0];
if ([viewController index] == 5)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
Within your UIPageViewController or viewController conforming to the UIPageViewControllerDelegate protocol:
// UIPageViewController.h
#interface UIPageViewController
#property (nonatomic, assign) NSInteger pageIndex;
#end
// UIPageViewController.m
#implementation UIPageViewController
/* ... */
- (void)viewDidLoad
{
[super viewDidLoad];
// Start with animation from the presenting view/item.
UIViewController *singlePageVC = [UIViewController viewControllerWithPageIndex:self.pageIndex];
singlePageVC.view.alpha = 0.0f;
if (singlePageVC != nil) {
self.dataSource = self;
self.delegate = self;
// Add singlePageVC to the page view controller with the initial pageIndex.
[self setViewControllers:#[ singlePageVC ]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
}
}
#pragma mark - UIPageViewControllerDelegate
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
NSInteger index = singlePageVC.pageIndex;
// This will add a page before the current view controller.
// Even if the index is 0, a new (dismissing) view controller will be added here.
return [UIViewController viewControllerWithPageIndex:index - 1];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
NSInteger index = singlePageVC.pageIndex;
// This will add a page after the current view controller.
// Even if the index is at your array bounds (i.e., 5), a new (dismissing) view controller will be added here.
return [UIViewController viewControllerWithPageIndex:index + 1];
}
/* ... */
#end
// UIViewController+PageIndex.h
extern NSString * const UIViewControllerShouldDismissFromPageViewControllerNotification;
#interface UIViewController (PageIndex)
#property (nonatomic, assign) NSInteger pageIndex;
- (instancetype)initWithPageIndex:(NSInteger)pageIndex;
+ (instancetype)viewControllerWithPageIndex:(NSInteger)pageIndex;
#end
// UIViewController+PageIndex.m
NSString * const UIViewControllerShouldDismissFromPageViewControllerNotification = #"UIViewControllerShouldDismissFromPageViewControllerNotification";
#implementation UIViewController (PageIndex)
// Initialize with the current item's index.
- (instancetype)initWithPageIndex:(NSInteger)pageIndex
{
self = [super init];
if (self) {
_pageIndex = pageIndex;
}
return self;
}
+ (instancetype)viewControllerWithPageIndex:(NSInteger)pageIndex
{
return [[self alloc] initWithPageIndex:pageIndex];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Check for pageIndex out of bounds of your array.
// This will be either less than 0 or greater or equal to your array count.
if (self.pageIndex < 0 || self.pageIndex >= array.count) { // Replace with your global or passed array here.
// Post dismiss notification here.
[[NSNotificationCenter defaultCenter]
postNotificationName:UIViewControllerShouldDismissFromPageViewControllerNotification
object:nil];
return;
}
}
#end
// View controller than manages your UIPageViewController.
#implementation YourViewController
/* ... */
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(dismissPageViewController:)
name:UIViewControllerShouldDismissFromPageViewControllerNotification
object:nil];
}
- (void)dismissPageViewController:(NSNotification *)notification
{
// Dismiss your page view controllers here.
[self.pageViewController dismissViewController];
self.pageViewController = nil;
// Or remove the page view controllers from its parent view controller,
[self.pageViewController willMoveToParentViewController:nil];
[self.pageViewController removeFromParentViewController];
[self.pageViewController didMoveToParentViewController:nil];
// Or its view from its super view.
[self.pageViewController.view removeFromSuperview];
self.pageViewController = nil;
}
/* ... */
#end

UIPageViewController switch between static view controllers and keep(reuse) contents

I have three view controllers: Settings,Chatbox and QuickMsg.
I want to use UIPageViewController to switch between these three view controllers. For each view controller, I don't want to re-initiate the view controller between swiping pages since it contains user inputed data and I want to persist the data. I defined three view controllers as static variables.
The screen goes black when I swipe between pages. Where have I done wrong?
Thanks a lot in advance!
ChatboxController, SettingController and QuickMsgController are three UIViewController controllers.
PagingViewController.h
#import <UIKit/UIKit.h>
#import "ChatboxController.h"
#import "SettingController.h"
#import "PagingViewController.h"
#import "QuickMsgController.h"
#class ChatboxController;
#class SettingController;
#class QuickMsgController;
#interface PagingViewController : UIViewController<UIPageViewControllerDataSource>
#property (strong, nonatomic) UIPageViewController *pageController;
#property (assign, nonatomic) NSInteger index;
+(ChatboxController*) getChatboxController;
+(SettingController*) getSettingsController;
#end
PagingViewController.m
#import "PagingViewController.h"
#interface PagingViewController ()
#end
#implementation PagingViewController
static ChatboxController* chatboxViewController;
static SettingController* settingViewController;
static QuickMsgController* quickMessageViewController;
int defaultIndex=1;
NSArray *viewControllers;
+(ChatboxController*) getChatboxController
{
return chatboxViewController;
}
+(SettingController*) getSettingsController
{
return settingViewController;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
self.index--;
if(self.index<0)
{
self.index=0;
}
return [self viewControllerAtIndex:self.index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
self.index++;
return [self viewControllerAtIndex:self.index];
}
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index {
NSLog(#"index:%d",index);
if(index==1)
{
return chatboxViewController;
}
else if (index==0){
return settingViewController;
}else if(index==2)
{
NSLog(#"Quick Message");
return quickMessageViewController;
}else{
NSLog(#"error here");
return nil;
}
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
// The number of items reflected in the page indicator.
return 3;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
// The selected item reflected in the page indicator.
return defaultIndex;
}
- (void)viewDidLoad
{
[super viewDidLoad];
chatboxViewController= [self.storyboard instantiateViewControllerWithIdentifier:#"chatbox"];
settingViewController= [self.storyboard instantiateViewControllerWithIdentifier:#"settings"];
quickMessageViewController=[self.storyboard instantiateViewControllerWithIdentifier:#"quickmessages"];
self.index=defaultIndex;
// [self performSelector:#selector(loadingNextView) withObject:nil afterDelay:2.0f];
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
NSArray *viewControllers = [NSArray arrayWithObject:chatboxViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Put your content view controllers in an NSArray:
#property (nonatomic, strong) NSArray *contentViewControllers;
Use:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [self.contentViewControllers indexOfObject:viewController];
if (index == 0) {
return nil;
}
return self.contentViewControllers[index - 1];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [self.contentViewControllers indexOfObject:viewController];
if (index >= self.contentViewControllers.count - 1) {
return nil;
}
return self.contentViewControllers[index + 1];
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
return self.contentViewControllers.count;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
return 0;
}

Resources