How to properly setup a UIPageViewController - ios

I have added a UIPageViewController to my project for creating a walkthrough screen, but I can seem to understand how to make this to work using storyboards.
First I have defined the two data source methods: viewControllerBeforeViewController and viewControllerAfterViewController.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [(WalktroughScreenPageViewController *)viewController index];
if (index == 0) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [(WalktroughScreenPageViewController *)viewController index];
index++;
if (index == 3) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (WalktroughScreenPageViewController *)viewControllerAtIndex:(NSUInteger)index {
WalktroughScreenPageViewController *childViewController = [[WalktroughScreenPageViewController alloc] init];
childViewController.index = index;
return childViewController;
}
Also for showing the dots at the bottom I added this two methods:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return 3;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
What I can understand it's how to do the setup in ViewDidLoad. I tried something like this which gives me a EXC_BAD_ACCESS error:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = self;
WalktroughScreenPageViewController *ic = [self viewControllerAtIndex:0];
[self setViewControllers:#[ic] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
}
Can someone point me to the right direction ?

Related

UIPageViewController conditionally Go to the next page

I've UIPageViewController and I can go next and previous page using below delegate methods.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[PageController class]])
return nil;
if(_counter < 1) {
return nil;
}
self.view.backgroundColor = [UIColor lightGrayColor];
return [self.storyboard instantiateViewControllerWithIdentifier:#"memoTable"];
}
Edit:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
//NSLog(#"Child controller count = %lu",(unsigned long)pageViewController.childViewControllers.count);
if ([viewController isKindOfClass:[PageController class]])
return nil;
if([viewController isKindOfClass:[TableViewController class]])
{
TableViewController *viewController1 = (TableViewController*) viewController;
if (viewController1.dataArr.count < 5) {
return nil;
}
}
return [self.storyboard instantiateViewControllerWithIdentifier:#"pageView"];
}
Now I want to go next page if some condition satisfied. Like last page viewController.isCheck == true.
How can I achieve this?
You can just add that condition to one of the existing ifs and return nil if the condition isn't verified. Something like:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[PageController class]] || ((YourViewControllerClass *)viewController).isCheck == false)
return nil;
return [self.storyboard instantiateViewControllerWithIdentifier:#"pageView"];
}
Simply check the view controller you instantiated before returning it in pageViewController:viewControllerAfterViewController method.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[PageController class]])
return nil;
let pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"pageView"];
if (pageViewController.isCheck)
return pageViewController;
else return nil;
}
My second guess is that you should try to instantiate all the view controllers that you will need in the UIPageViewController before you navigate.
UIViewController* memoViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"memoTable"];
UIViewController* pageViewContentController = [self.storyboard instantiateViewControllerWithIdentifier:#"pageView"];
assign all the view controllers you will need in an array
NSArray* reservedViewControllers = #[memoViewController, pageViewController];
use index to track where you current page is
int currentIndex = 0;
Then you can easily iterate between your view controllers in the UIPageViewControllerDataSource functions.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if(currentIndex == 0) return nil;
currentIndex = currentIndex - 1;
return reservedViewControllers[currentIndex];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if(currentIndex == reservedViewControllers.count) return nil;
currentIndex = currentIndex + 1;
//check for last page
if(currentIndex == reservedViewControllers.count){
UIViewController* lastViewController = reservedViewControllers[currentIndex];
if(lastViewController.isCheck == false) return nil;
}
return reservedViewControllers[currentIndex];
}
According to your question you should use the scroll view for this purpose.. set the content size of the scroll view and give the content offset to the content on your table...
for your reference:
see the link:
https://github.com/cwRichardKim/RKSwipeBetweenViewControllers
https://github.com/nicklockwood/SwipeView

How to remove infinite scroll on UIPageViewController?

I have two storyboards with identifiers "one" and "two", I have a UIPageViewController and a subclass of it. In the subclass I have this code below:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = self;
self.delegate = self;
counter = 0;
[self setViewControllers:#[[self.storyboard instantiateViewControllerWithIdentifier:#"one"]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[ViewController class]]){
return [self.storyboard instantiateViewControllerWithIdentifier:#"one"];
}
return nil;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[secondViewController class]]){
return [self.storyboard instantiateViewControllerWithIdentifier:#"two"];
}
return nil;
}
This code works very well, but has a problem! The scroll is infinite; in my case I need to take off the infinite scroll. How I can do this?
First thing is create a counter and do checks inside my methods:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if (counter < 1) {
NSLog(#"Don't get back");
return nil;
}
......
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if (counter >= 1) {
NSLog(#"Don't go anymore");
return nil;
}
....
}
The second thing is try to use counter++ and counter--. This is being a big puzzle to me, anyone have any better ideas?
If you only have two pages, then simply check when you get a request for a page, if the current page is not already the one you would serve. For this, you need a way to identify it.
If each of the pages has a different class, you can simply check for the class. Otherwise you'll need some other criterion.
If using custom classes:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[ClassOfSecondViewController class]])
{
return [self.storyboard instantiateViewControllerWithIdentifier:#"one"];
}
return nil;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[ClassOfFirstViewController class]])
{
return [self.storyboard instantiateViewControllerWithIdentifier:#"two"];
}
return nil;
}
Just a little change in the condition.
- (nullable UIViewController *)pageViewController:(nonnull UIPageViewController *)pageViewController viewControllerBeforeViewController:(nonnull UIViewController *)viewController {
NSUInteger currentIndex = [pages indexOfObject:viewController];
NSUInteger previousIndex = ((currentIndex - 1) % pages.count);
if (viewController == yourFirstViewController)
return nil;
return pages[previousIndex];
}
- (nullable UIViewController *)pageViewController:(nonnull UIPageViewController *)pageViewController viewControllerAfterViewController:(nonnull UIViewController *)viewController {
NSUInteger currentIndex = [pages indexOfObject:viewController];
NSUInteger nextIndex = ((currentIndex + 1) % pages.count);
if (viewController == yourLastViewController)
return nil;
return pages[nextIndex];
}

UIPageViewController does not show its content view controllers

I can't seem to get a UIPageViewController working. With the code below, the PageViewController calls all of its data source methods and even shows the corresponding dots, but the actual content views are not displayed (even though I checked in debug that the view controllers are instantiated correctly). Does anyone have a clue what I'm doing wrong?
EDIT: the view controller is not nil, but all its properties are. Apparently there is something going wrong with the instantiation.
//content view controller containing a text label
#import "TutorialPhoneContentViewController.h"
#interface TutorialRootViewController ()
#end
#implementation TutorialRootViewController {
NSInteger numberOfViewControllers;
}
- (void)viewDidLoad {
[super viewDidLoad];
numberOfViewControllers = 3;
[self setUpViewControllers];
}
- (void)setUpViewControllers {
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageViewController.dataSource = self;
TutorialPhoneContentViewController *ctrl = [self viewControllerAtIndex:0];
[self.pageViewController setViewControllers:#[ctrl] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self.pageViewController willMoveToParentViewController:self];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
- (TutorialPhoneContentViewController *)viewControllerAtIndex:(NSInteger)index {
//this returns the correct view controller, checked via debug
TutorialPhoneContentViewController *ctrl = [self.storyboard instantiateViewControllerWithIdentifier:#"tutorialContentViewController"];
ctrl.index = index;
ctrl.text = [NSString stringWithFormat:#"View Controller #%ld", index];
return ctrl;
}
#pragma mark - Page View Controller
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSInteger index = [(TutorialPhoneContentViewController *)viewController index];
if (index == 0) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSInteger index = [(TutorialPhoneContentViewController *)viewController index];
if (index == numberOfViewControllers-1) {
return nil;
}
index++;
return [self viewControllerAtIndex:index];
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
return numberOfViewControllers;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
return 0;
}

UIPageViewControllerDataSource calls viewControllerAfterViewController twice the first time running

I know this might be a duplicate but I tried other solutions found online and none of them seemed to solve my problem.
I have a pageViewController that displays 4 UIViewControllers.
At first scroll the pageController swipes to the third View controller but shows the second ViewController.
Here is my code:
_contentPageRestorationIDs = [[NSArray alloc]initWithObjects:#"PRPeripheralsOriantaionVC",#"PRSessionAnalysisDataVC", #"PRNavigationVC",#"PRVideoVC", nil];
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return self.contentPageRestorationIDs.count;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSString *vcRestorationID = viewController.restorationIdentifier;
NSUInteger index = [self.contentPageRestorationIDs indexOfObject:vcRestorationID];
NSLog(#"viewControllerBeforeViewController %#",vcRestorationID);
if (index == 0)
{
return nil;
}
index--;
return [self viewControllerAtIndex:index ];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSString *vcRestorationID = viewController.restorationIdentifier;
NSLog(#"viewControllerAfterViewController %#",vcRestorationID);
NSUInteger index = [self.contentPageRestorationIDs indexOfObject:vcRestorationID];
index ++;
if (index == self.contentPageRestorationIDs.count )
{
return nil;
}
return [self viewControllerAtIndex:index ];
}
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
{
NSLog(#"willTransitionToViewControllers %# %lu",pendingViewControllers,(unsigned long)index);
}
-(void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
NSLog(#"didFinishAnimating %#",previousViewControllers);
}
You have a bug, Update your datasource method:
h File
#interface ViewController : UIPageViewController
#end
m File
#import "ViewController.h"
#interface ViewController ()<UIPageViewControllerDataSource>
#property (strong, nonatomic) NSArray *pageColors;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.pageColors = #[#"green", #"yellow", #"black", #"red"];
self.dataSource = self;
UIViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Private method
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([self.pageColors count] == 0) || (index >= [self.pageColors count])) {
return nil;
}
NSString* pageColor = self.pageColors[index];
// Create a new view controller and pass suitable data.
UIViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:pageColor];
return pageContentViewController;
}
#pragma mark - Page View Controller Data Source
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
NSString * identifier = viewController.restorationIdentifier;
NSUInteger index = [self.pageColors indexOfObject:identifier];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
NSString * identifier = viewController.restorationIdentifier;
NSUInteger index = [self.pageColors indexOfObject:identifier];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageColors count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return [self.pageColors count];
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
#end
Don't forget to change for each view controller the Storyboard ID with his color ([#"green", #"yellow", #"black", #"red"]).
For example, for the read view controller:
You can find here very useful tutorial.
The correct way to do that is to define pageIndex property for each view controller, and then set it in the page view delegate methods, using the view controller at index method, your final wanted code should be the page view delegate methods, passing an index to the view controller at index path and returning there retrieved VC and that's it.

Blank Page after switching to UIPageViewControllerTransitionStyleScroll

I'm trying to switch my UIPageViewController's transition style from UIPageViewControllerTransitionStylePageCurl to UIPageViewControllerTransitionStyleScroll.
When using UIPageViewControllerTransitionStylePageCurl, Pages transition as desired. However, with UIPageViewControllerTransitionStyleScroll, blank pages mysteriously appear between page turns. The pattern with which blank pages show isn't consistent between builds.
Any idea what might be happening here? I'm stumped.
//AppDelegate.m
PageVC *pageVC = [[PageVC alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:NULL];
//PageVC.h
#interface PageVC : UIPageViewController <UIPageViewControllerDataSource>
//PageVC.m
- (void)viewDidLoad {
[super viewDidLoad];
self.graphVC = [[GraphVC alloc]init];
self.aboutVC = [[AboutVC alloc] init];
self.exerciseVC = [[ExerciseVC alloc] init];
self.viewControllerArray = [[NSArray alloc] initWithObjects:self.exerciseVC, self.graphVC, self.aboutVC, nil];
self.currentPage = 1;
if (self.graphVC != nil) {
self.dataSource = self;
[self setViewControllers:#[self.graphVC]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
}}
- (UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
if (self.currentPage >= 2) {
return nil;
} else {
self.currentPage += 1;
return [self.viewControllerArray objectAtIndex:self.currentPage];
}
}
- (UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if (self.currentPage <= 0) {
return nil;
} else {
self.currentPage -= 1;
return [self.viewControllerArray objectAtIndex:self.currentPage];
}
}
As #pbasdf worote, tracking currentPage in viewControllerAfter/Before was a bad idea. When using TransitionStyleScroll, these two methods are called multiple times per page turn. As a result, they sometimes incremented incorrectly and returned nil. (TransitionStylePageCurl only calls each method once, after a page turn.)
A better solution is to check for the viewController passed to each method, and return before and after accordingly:
// PageVC.m
- (void)viewDidLoad {
[super viewDidLoad];
// initialize array of ViewControllers
viewControllerArray = [[NSArray alloc] initWithObjects:exerciseVC, graphVC, aboutVC, nil];
}
- (UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [viewControllerArray indexOfObject:viewController];
if (index >= [viewControllerArray count]-1) {
return nil;
} else {
return [viewControllerArray objectAtIndex:index+1];
}
}
- (UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [viewControllerArray indexOfObject:viewController];
if (index <= 0) {
return nil;
} else {
return [viewControllerArray objectAtIndex:index-1];
}

Resources