I'm trying to add a UIPageViewController to my App using following code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *viewControllers = #[page0,
page1,
page2];
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:nil];
self.pageController.dataSource = self;
self.pageController.view.frame = self.view.frame;
[self.pageController setViewControllers:#[[viewControllers firstObject]]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
[self.pageController willMoveToParentViewController:self];
[self addChildViewController:self.pageController];
[self.view addSubview:self.pageController.view];
[self.pageController didMoveToParentViewController:self];
}
Then problem is that when I run the application, using the Simulator or a physical device, I get a empty screen. If I try to swipe the views I can see that the page that I actually added to the viewControllers array is already there but outside of the screen. Making some debugging with SparkInspector, I can see that the view is actually there but with an offset with the same value equal to the screen width.
I also have implemented the DataSource for the UIPageViewController. Here are the implementation of the delegated methods:
- (NSInteger) presentationCountForPageViewController:(UIPageViewController *)pageViewController {
return 3;
}
- (NSInteger) presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
return 0;
}
- (UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
if (viewController == nil) {
return [viewControllers firstObject];
}
NSUInteger index = [viewControllers indexOfObject:viewController];
// NSParameterAssert(index != NSNotFound);
index++;
if (index == viewControllers.count) {
return nil;
}
return [viewControllers objectAtIndex:index];
}
- (UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if (viewController == nil) {
return [viewControllers firstObject];
}
NSUInteger index = [viewControllers indexOfObject:viewController];
if (index == 0) {
return nil;
}
index--;
return [viewControllers objectAtIndex:index];
}
I cannot see why the App is offsetting the view controllers, so any advice will be very helpful.
Related
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;
}
I need a dynamic UIPageViewController, the number of pages depends on the quantity of items of my array.
I have two views for this. One for the first page and another one for the other pages. It will be always more than 2 pages (two items on my array).
I'm trying to test with fixed values. But the paging it's not right. The index is not correct.
1st problem, the presentationCountForPageViewController [_vc count] returns nil;
2nd problem, the pagination it's not right. I get to the last page, and the first view it's gone, so if I go back, goes to second view, always...
Please, help!
- (void)viewDidLoad {
[super viewDidLoad];
FirstViewController *agenda = (FirstViewController*)[self viewControllerAtIndex:0];
[_vc addObject:#"FirstViewController"];
[_vc addObject:#"SecondViewController"];
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
NSArray *viewControllers = [NSArray arrayWithObject:agenda];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
UIPageControl *pageControlAppearance = [UIPageControl appearanceWhenContainedIn:[UIPageViewController class], nil];
pageControlAppearance.pageIndicatorTintColor = [UIColor lightGrayColor];
pageControlAppearance.currentPageIndicatorTintColor = [UIColor darkGrayColor];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [_vc indexOfObject:viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
//notice here I call my instantiation method again essentially duplicating work I have already done!
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [_vc indexOfObject:viewController];
if (index == NSNotFound) {
return nil;
}
index++;
return [self viewControllerAtIndex:index];
}
#pragma mark - Page View Controller Data Source
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (index == 0){
FirstViewController *firstController = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstViewController"];
return firstController;
}
else{
SecondViewController *secondController = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
return secondController;
}
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return [_vc count];
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
As per comments/chat, a few things needed resolving:
You need to instantiate the _vc array
You need to add UIViewControllerobjects to this array, not the strings you currently use
You need to amend the viewControllerBeforeViewController and viewControllerAfterViewController methods to return the relevant element from the _vc array, remembering to test that the index remains within the array's bounds, to prevent swiping beyond the final page, or before the first (agenda) page.
I have searched a lot before posting without success:
I have UIPageViewController with array of UIViewController and PageCurl as transition.
Every UIViewController consists of UIScrollView and UIWebView.
the scroll height calculated based on webview height after loading the content.
now when I load UIPageViewController and start navigation between UIViewController without scrolling the content every thing works fine. But if I scrolled any page and then navigate to the next page or previous one. it will redisplay the same page and then works again normally.
I have tested it on IOS7 and IOS8:
UIPageViewController viewDidLoad method:
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.pageController.doubleSided = NO;
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
View Controller webViewDidFinishLoad:
CGSize contentSize = aWebView.scrollView.contentSize;
webView.frame = CGRectMake(0, 312, contentSize.width, contentSize.height);
scrollView.contentSize = CGSizeMake(320.f, contentSize.height+60);
Edit
Here is the complete code for viewControllerBeforeViewController and viewControllerAfterViewController of UIPageViewController
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [(NewsDetailVC *)viewController index];
self.index = index;
NSLog(#"view before %i", index);
if (index <= 0) {
return nil;
}
index--;
currentViewController = [self viewControllerAtIndex:index];
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [(NewsDetailVC *)viewController index];
NSLog(#"view after %i", index);
if (index >= self.newsList.count -1) {
return nil;
}
index++;
currentViewController =[self viewControllerAtIndex:index];
return [self viewControllerAtIndex:index];
}
Edit2
The previous page is an UIViewController contains UITableView when click on a cell will pass the array of Items to UIPageViewController and the selected index.
The purpose of currentViewController because I have controls for font size, and current view background.
The UIPageViewController parent is UIViewController
Here is the code of viewControllerAtIndex:
- (NewsDetailVC *)viewControllerAtIndex:(NSUInteger)index {
NewsDetailVC *childViewController = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"NewsDetailVC"];
childViewController.index = index;
childViewController.selectedNews = [self.newsList objectAtIndex:index ];
childViewController.selectedCategory = self.selectedCategory;
return childViewController;
}
I had the same problem with an UIPageViewController.
It seems like the index shown in viewControllerAtIndex is not quite an accurate one.
As somebody else mentioned in the comment above, in these situations you should implement the delegate method pageViewController:willTransitionToViewControllers.
In this method you should do your work:
- (void)pageViewController:(UIPageViewController *)pageViewController
willTransitionToViewControllers:(NSArray *)pendingViewControllers {
if ([pendingViewControllers count] > 0) {
NSUInteger index = [(PageContentViewController *)
[pendingViewControllers objectAtIndex:0] pageIndex];
if (index == [yourArray count] - 1 || index == 0) {
[self loadContent];
} else {
[self doOtherStuff];
}
}
}
Somehow, the * viewControllerAtIndex* is called several times, giving you troubles finding the index, but in this method the index should be accurate.
Hope it helps!
I need to perform the transitions of the uipageviewcontroller from right to left. So I look at this and this examples. But difference is I have one viewcontroller to show at a time and pageViewController:spineLocationForInterfaceOrientation: can only return UIPageViewControllerSpineLocationMin not UIPageViewControllerSpineLocationMax. This is what I have done so far.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [(APPChildViewController *)viewController index];
if (index == 0) {
return nil;
}
// Decrease the index by 1 to return
index--;
return [self viewControllerAtIndex:index];}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [(APPChildViewController *)viewController index];
index++;
if (index == 15) {
return nil;
}
return [self viewControllerAtIndex:index];}
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
UIViewController *currentViewController = [self.pageController.viewControllers objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
self.pageController.doubleSided = NO;
//Return the spine location
return UIPageViewControllerSpineLocationMin;
}
How can I get the curl working properly with the UIPageViewControllerSpineLocationMin?. Also I cannot use Page-Based Application template.
Based on this I found the solution. This is the code.
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
UIViewController *currentViewController = [self.pageController.viewControllers objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
self.pageController.doubleSided = NO;
return UIPageViewControllerSpineLocationMax;
}
I'm learning iOS programming from a book, and I've coded an app to make a page turn to another page, similar to iBooks. But when I run it in the simulator, when I swipe to the left or right the page doesn't turn or transition at all. I don't know why.
My code that I believe is relevant is below. I've also attached the whole project in case I've missed anything. Basically there's a UIWebview that is the content for the UIViewController. It gets populated from the model located in the class that acts as the data source for the PageView. Depending on which view controller you go to the appropriate (protocol) method then sets the content in the webview according to the index of the view controller you're going to.
But, it won't work, as said:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [self indexOfViewController:(ContentViewController *)viewController];
if (index == 0 || index == NSNotFound) {
return nil;
}
else {
index--;
return [self viewControllerAtIndex:index];
}
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [self indexOfViewController:(ContentViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
else {
index--;
return [self viewControllerAtIndex:index];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self createContentPages];
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin]
forKey:UIPageViewControllerOptionSpineLocationKey];
_pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options: options];
_pageController.dataSource = self;
[[_pageController view] setFrame:[[self view] bounds]];
ContentViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[_pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
[self addChildViewController:_pageController];
[[self view] addSubview:[_pageController view]];
[_pageController didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)createContentPages {
NSMutableArray *pageStrings = [[NSMutableArray alloc] init];
for (int i = 1; i <= 10; i++) {
NSString *contentString = [[NSString alloc] initWithFormat:#"<html><head></head><body><h1>Chapter %d</h1><p>This is the page %d of content displayed using UIPageViewController in iOS 6.</p></body></html>", i, i];
[pageStrings addObject:contentString];
}
self.pageContent = [[NSArray alloc] initWithArray:pageStrings];
}
// Return the data view controller for the given index
- (ContentViewController *)viewControllerAtIndex:(NSUInteger)index {
if (([self.pageContent count] == 0) || index >= [self.pageContent count]) {
return nil;
}
else {
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:[NSBundle mainBundle]];
ContentViewController *dataViewController = [storyBoard instantiateViewControllerWithIdentifier:#"contentView"];
dataViewController.dataObject = self.pageContent[index];
return dataViewController;
}
}
- (NSUInteger)indexOfViewController:(ContentViewController *)viewController {
return [self.pageContent indexOfObject:viewController.dataObject];
}
Whole project: http://cl.ly/1B0l3Z1H1616
In the
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
method, you have
index--
Try changing it to
index++