I have a UIPageViewController that displays two UIViewControllers and allows the user to swipe between the two. I implemented a button that allows the user to manually switch between the two. The code to execute this is as follows:
[self setViewControllers:#[[self.storyboard instantiateViewControllerWithIdentifier:#"PlayersViewController"]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^(BOOL completion){}];
Unfortunately, every time I press the button, I get an exception that says:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
I call the method from my custom class that extends the UIPageViewControllerDelegate.
Any help would be much appreciated!
**Edit:
Here are relevant portions of my custom class:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = self;
gvc = [[GamesViewController alloc] init];
pvc = [[PlayersViewController alloc] init];
[self setViewControllers:#[[self.storyboard instantiateViewControllerWithIdentifier:#"GamesViewController"]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
- (void) slidepage
{
[self setViewControllers:#[[self.storyboard instantiateViewControllerWithIdentifier:#"PlayersViewController"]] direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:^(BOOL completion){NSLog(#"hihi");}];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[GamesViewController class]])
return nil;
return [self.storyboard instantiateViewControllerWithIdentifier:#"GamesViewController"];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[PlayersViewController class]])
return nil;
return [self.storyboard instantiateViewControllerWithIdentifier:#"PlayersViewController"];
}
It seems that
[self.storyboard instantiateViewControllerWithIdentifier:#"PlayersViewController"]
returns nil. This would mean that either self.storyboard is nil or the storyboard can't find a view controller for the given identifier.
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 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 gonna make a tutorial page using UIPageViewController.
But crash happened. I couldn't find the point occurring crash.
I'm trying to use UIPageViwController directly, not using in UIViewController.
dataSource works. but when trying to call [self setViewControllers:#[pageZero] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
, an error occurred like the below.
UserGuidePageViewController is linked to UIPagveViewController Frame in starboard.
Following is Header
#import <UIKit/UIKit.h>
#interface UserGuidePageViewController : UIPageViewController <UIPageViewControllerDataSource> {
}
#end
Following is code
#import "UserGuidePageViewController.h"
#import "UserGuideViewController.h"
#interface UserGuidePageViewController () {
}
#property (nonatomic, retain) NSArray *array;
#end
#implementation UserGuidePageViewController
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.dataSource = self;
}
return self;
}
- (UserGuideViewController *)userGuideViewForPageIndex:(NSUInteger)pageIndex
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UserGuideViewController *targetViewController = [storyboard instantiateViewControllerWithIdentifier:#"UserGuideViewController"];
return targetViewController;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UserGuideViewController *pageZero = [self userGuideViewForPageIndex:0];
[self setViewControllers:#[pageZero] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
--> Crash happened here!!!
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Page View Controller Data Source
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [(UserGuideViewController *)viewController pageIndex];
return [self userGuideViewForPageIndex:(index - 1)];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [(UserGuideViewController *)viewController pageIndex];
return [self userGuideViewForPageIndex:(index + 1)];
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
// The number of items reflected in the page indicator.
return 1;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
// The selected item reflected in the page indicator.
return 0;
}
Error is following.
*** Assertion failure in -[_UIQueuingScrollView setView:direction:animated:completion:], /SourceCache/UIKit/UIKit-2903.23/_UIQueuingScrollView.m:671
2013-12-18 15:23:35.779 Natural Calendar[4497:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: view'
*** First throw call stack:
(0x30ef6f4b 0x3b6d36af 0x30ef6e25 0x3189efe3 0x33ba3321 0x33b382eb 0x33b38407 0xf4715 0x3366e37b 0x3366e139 0xf0c45 0x1062d9 0x1106c3 0x3369e713 0x3369e6b3 0x3369e691 0x3368a11f 0x3369e107 0x3369ddd9 0x33698e65 0x3366e79d 0x3366cfa3 0x30ec2183 0x30ec1653 0x30ebfe47 0x30e2ac27 0x30e2aa0b 0x35b0b283 0x336ce049 0xecd09 0x3bbdbab7)
libc++abi.dylib: terminating with uncaught exception of type NSException
Actually, I changed into Xcode template style.
But this error is also occurred.
- (void)viewDidLoad
{
[super viewDidLoad];
_pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
[_pageViewController setDataSource:self];
UserGuideViewController *pageZero = [self userGuideViewForPageIndex:0];
[_pageViewController setViewControllers:#[pageZero] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
[self addChildViewController:_pageViewController];
[self.view addSubview:[_pageViewController view]];
CGRect pageViewRect = self.view.bounds;
//pageViewRect = CGRectInset(pageViewRect, 40.0, 40.0);
_pageViewController.view.frame = pageViewRect;
[_pageViewController didMoveToParentViewController:self];
}
I don't know what's problem. When choosing UIPageViewControllerTransitionStyleScroll, the error occurred. When choosing UIPageViewControllerTransitionStylePageCurl, the error is not happened.
#interface UserGuidePageViewController : UIPageViewController <UIPageViewControllerDataSource> {
chnge this line with
#interface UserGuidePageViewController : UIViewController <UIPageViewControllerDataSource> {
Means you can use UIViewcontroller for this
see this link for reference.
and this another link which is used storyboard.
I found loadView() { } declared at page ContentViewController. However loadView() remains empty without calling [super loadView];. As a result from this, the view would remain nil.
Now it appears to work after the removal of the method, loadView().
I have a page view controller and I am getting the error Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: [views count] == 3'
Here Is My Datasource:
//
// MSABDrawingPageModelController.m
// My Special Alphabet Book
//
// Created by Ari Porad on 9/3/13.
// Copyright (c) 2013 Cari Books. All rights reserved.
//
#import "MSABDrawingPageModelController.h"
#import "MSABDrawingViewController.h"
#import "MSABDrawingView.h"
#implementation MSABDrawingPageModelController
-(id)initWithLetter:(MSABLetter *)letter{
if (self = [super init]) {
self.letter = letter;
}
return self;
}
-(id)init{
return [self initWithLetter:nil];
}
-(NSURL *)docsDirUrl{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0];
}
-(UIViewController *)viewControllerAtIndex:(int)index{
MSABDrawingViewController *VC = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"MSAB_DRAWING_VIEW_CONTROLLER"];
((MSABDrawingView *) VC.view).points = [NSMutableArray arrayWithArray:#[[NSMutableArray array]]];
VC.index = index;
VC.letter = self.letter;
return VC;
}
#pragma mark -
#pragma mark Page View Controller Data Source Methods
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController{
UIViewController *vc = [self viewControllerAtIndex:((MSABDrawingViewController *)viewController).index + 1];
if(vc == nil){
vc = [[MSABDrawingViewController alloc] init];
}
NSLog(#"AVC: %#", vc);
return vc;
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController{
UIViewController *vc = [self viewControllerAtIndex:((MSABDrawingViewController *)viewController).index - 1];
if(vc == nil){
vc = [[MSABDrawingViewController alloc] init];
}
NSLog(#"PVC: %#", vc);
return vc;
}
#end
Let me know if there is anything else you need to know.
I had the same problem.
It turns out you have to set the uipageviewcontroller's initial view controller/s whether you set it through IB or programatically.
Source:
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/PageViewControllers.html#//apple_ref/doc/uid/TP40011313-CH4-SW10
Call the setViewControllers:direction:animated:completion: method and set your array of initial view controller/s.