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.
Related
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];
}
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];
}
I'm having so much trouble creating a pageview controller. I have made adjustments to my code three times but i KEEP getting the following error: ' Expected selector for Objective C method'. I also get another error message which says Missing '#end'. Could anyone please tell me why I recieve these messages? :l
#import "InstructionContentViewController.h"
#import "StorageViewController.h"
#interface StorageViewController ()
#end
#implementation StorageViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the data model
_pageTitles = #[#"Over 200 Tips and Tricks", #"Discover Hidden Features", #"Bookmark Favorite Tip", #"Free Regular Update"];
_pageImages = #[#"instructions1.png", #"instructions2.png", #"instructions3.png", #"instructions4.png"];
// Create page view controller
self.instructionViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"InstructionViewController"];
self.instructionViewController.dataSource = self;
InstructionContentViewController *startInstructViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startInstructViewController];
[self.instructionViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
// Change the size of page view controller
self.instructionViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 50);
[self addChildViewController:_instructionViewController];
[self.view addSubview:_instructionViewController.view];
[self.instructionViewController didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)startWalkthrough:(id)sender {
InstructionContentViewController *startingInstructViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingInstructViewController];
[self.instructionViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
#pragma mark - Page View Controller Data Source Methods:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = ((InstructionContentViewController*) viewController).pageIndex;
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = ((InstructionContentViewController*) viewController).pageIndex;
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageTitles count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (InstructionContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([self.pageTitles count] == 0) || (index >= [self.pageTitles count])) {
return nil;
}
// Create a new view controller and pass suitable data.
InstructionContentViewController *instructionContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"InstructionContentViewController"];
instructionContentViewController.instructimageFile = self.pageImages[index];
instructionContentViewController.instructtitleText = self.pageTitles[index];
instructionContentViewController.pageIndex = index;
return instructionContentViewController;
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return [self.pageTitles count];
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
#end
it is possible that a syntax error exists in one of the files you #import, either InstructionContentViewController.h or StorageViewController.h.
Due to how imports are handled, the warning can sometimes show up in unexpected place. Verify that those are well-formed, as well as any other files that may be imported from those.
Also, within Xcode you can click on the "Product" menu and hold the Option key for a 'Clean Build Folder' command. This will clear out pre-compiled data and force everything to be recompiled from scratch.
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;
}
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 ?