UIPageViewController, instantiating ViewControllers with Storyboard ID, Objective-C - ios

I am trying to implement UIPageViewController and I believe that I am in the last stage. At first the UIPageViewController only used one VC to scroll to a "different" page. Now i want it to scroll between different ViewControllers.
This is the code i am using:
- (void)viewDidLoad {
[super viewDidLoad];
self.vcIdentifiers = #[#"PageContentViewController", #"PageContentViewController1"];
// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
UIViewController *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 - 30);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
- (PageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([self.vcIdentifiers count] == 0) || (index >= self.vcIdentifiers.count)) {
return nil;
}
// Create a new view controller and pass suitable data.
PageContentViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageContentViewController"];
pageContentViewController.titleText = self.pageTitles[index];
pageContentViewController.pageIndex = index;
return pageContentViewController;
}
I belive it is in the viewControllerAtIndex i need to make some changes. I thought of doing this:
PageContentViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:self.vcIdentifiers[index]];
But then it still would initialise the PageContentViewController. So my problem is i don't know how to initialise the different ViewControllers.

Related

IOS:Showing WebView through PageViewController

in my project i am using UIPageViewController to show a Strip of videos on home page. for showing videos i uses UIWebView on UIPageViewController.
initially i use UIImageView on it and it is working fine but when i applied UIWebView it gives error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
please some help me to resolve this issue
i am sharing my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Create the data model
//_pageTitles = #[#"Over 200 Tips and Tricks", #"Discover Hidden Features", #"Bookmark Favorite Tip", #"Free Regular Update"];
//_pageImages = #[#"https://www.youtube.com/embed/uObzRhWq7tk", #"Radio_Button-512.png", #"page3.png", #"page4.png"];
_videourl = #[#"https://www.youtube.com/embed/uObzRhWq7tk",#"https://www.youtube.com/embed/38DZgGjaPOA"];
// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
PageContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
// Change the size of page view controller
self.pageViewController.view.frame = CGRectMake(0, CGRectGetMaxY(searchbtn.frame)+80, self.view.frame.size.width, self.view.frame.size.height/3);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
and
- (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.urlFile = self.videourl[index];
pageContentViewController.pageIndex = index;
return pageContentViewController;
}
The error says you are trying to insert nil into array. Set up a breakpoint before this line:
NSArray *viewControllers = #[startingViewController];
startingViewController is probably nil.

Next Page Button for UIPageViewController iOS

I followed http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/ for creating a PageViewController.
This tutorial contains :
(code for Restart button)
- (IBAction)restartButton:(id)sender {
PageContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
When i tried for Next button like this:
- (IBAction)nextPageButton:(id)sender {
PageContentViewController *startingViewController = [self viewControllerAtIndex:+1];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
but it fails, how to perform next page action with the UIButton that responds with the PageControl till last page.
That wont work because you are using +1, instead, try this:
//get current index of current page
DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSUInteger retreivedIndex = [self.modelController indexOfViewController:theCurrentViewController];
//check that current page isn't first page
if (retreivedIndex < 5){
//get the page to go to
DataViewController *targetPageViewController = [self.modelController viewControllerAtIndex:(retreivedIndex + 1) storyboard:self.storyboard];
//put it(or them if in landscape view) in an array
NSArray *theViewControllers = nil;
theViewControllers = [NSArray arrayWithObjects:targetPageViewController, nil];
//add page view
[self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
}
PageNumberToJump=2
-(void)buyAction
{
isFromBuy = YES;
APPChildViewController *initialViewController = [self viewControllerAtIndex:PageNumberToJump];
viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
-(NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
if (isFromBuy)
{
isFromBuy = NO;
return PageNumberToJump;
}
return 0;
}

Reloading Views in UIPageViewController

I am making an iOS application that has a UIPageViewController that is loaded with a default page if there aren't any others added yet, and a user can add pages as he progresses through the app. However, the initial view never changes as others are added.
I've tried the answers from these questions:
Is it possible to Turn page programmatically in UIPageViewController?
Changing UIPageViewController's page programmatically doesn't update the UIPageControl
Removing a view controller from UIPageViewController
Refresh a UIPageViewController
None of them work for me. The view still remains at the initial page until I reload the application, after which, the pages show fine, and I can add as many as I want.
This is the code I have for making the PageView and the pages
- (void)viewDidLoad
{
[super viewDidLoad];
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self pageControllerView] bounds]];
UIViewController *initialViewController = [self viewControllerAtIndex:0];
__block Page1ViewController *blocksafeSelf = self;
// This doesn't work either... :(
void (^completionBlock)(BOOL) = ^(BOOL finished)
{
// It seems that whenever the setViewControllers:
// method is called with "animated" set to YES, UIPageViewController precaches
// the view controllers retrieved from it's data source meaning that
// the dismissed controller might not be removed. In such a case we
// have to force it to clear the cache by initiating a non-animated
// transition.
usleep(1);
dispatch_async(dispatch_get_main_queue(), ^(){
[blocksafeSelf.pageController setViewControllers:#[initialViewController]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
});
};
[self.pageController setViewControllers:#[initialViewController]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:completionBlock];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
}
- (void)reloadViews
{
[[self childViewControllers] makeObjectsPerformSelector:#selector(removeFromParentViewController)];
[[self.view subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
[self.pageController willMoveToParentViewController:nil];
[self.pageController removeFromParentViewController];
[self.pageController.view removeFromSuperview];
self.pageController = nil;
self.pageController = [[UIPageViewController alloc] init];
[self.view setNeedsDisplay];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = ((SomethingViewController *)viewController).index;
index++;
if (index >= [Settings somethings].count)
{
return nil;
}
return [self viewControllerAtIndex:index];
}
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Storyboard_iPhone"
bundle:nil];
if ([Settings somethings].count == 0 && index == 0)
{
NoSomethingViewController *vc = [sb instantiateViewControllerWithIdentifier:#"NoSomethingViewController"];
vc.index = index;
return vc;
}
SomethingViewController *childViewController = [sb instantiateViewControllerWithIdentifier:#"SomethingViewController"];
childViewController.something = [[Settings somethings] somethingAtIndex:index];
childViewController.index = index;
return childViewController;
}
When a new page should be created, by adding a something in the somethings array, I call reloadViews from the View controller that did the adding of the something. The code then hits the second part of viewControllerAtIndex, but that is not shown on the page that I see on the phone, as I still see the NoSomethingViewController and it's view.
If you need more code, I'll provide.
add the following code in the pageViewController viewWillAppear method
dataSource = self
and in the viewWillDisappear add
dataSource = nil
this will force the pageViewController to reload its dataSource
To show the current dot, dont forget you have to update the page indicator
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
Example how to update your UIPageControl indicator https://stackoverflow.com/a/22576250/3897011
After a long time and after wasting a support ticket with Apple, I decided to go with another approach.
In my Storyboard I created 2 different navigation controllers, one for each case (if there are somethings and if there aren't any), and I have an initial view Controller that, on viewDidAppear, instantiates one of the two and presents it. This way, I make sure that the entire navigation controller gets reloaded correctly.
UINavigationController *nc;
if ([Settings somethings].count)
{
nc = [self.storyboard instantiateViewControllerWithIdentifier:#"NavigationController"];
}
else
{
nc = [self.storyboard instantiateViewControllerWithIdentifier:#"NoSomethingNavigationController"];
}
self.nc = nc;
[self presentViewController:nc animated:NO completion:nil];
In order to reload UIPageViewController you can use the following code:
[self.pageController setViewControllers:#[yourCurrentController]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
Also please implement
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
method.

Issue with iOS7 and UIPageViewController

I have a UIPageViewController that is added as subview on UIView. The UIView sits on the top of UIViewController's view, at start it puts the UIPageViewController lower than it should sit, but right after I click on the next button to push the next UIPageViewController's view, it send it back to the correct position.
What is he doing that? I tried everything but still can figure out the problem.
This is how it looks:
What seems to be the problem? Thanks in advance!
EDIT: This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
DataChildViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.pageController.dataSource = self;
self.pageController.delegate = self;
self.pageController.view.userInteractionEnabled = NO;
self.pageController.view.frame = self.view.bounds;
[self.pageControllerParent addSubview:self.pageController.view];
[self addChildViewController:self.pageController];
self.currentPageControllerIndex = 1;
}
- (IBAction)buttonNextDidClicked
{
self.buttonNext.enabled = NO;
__weak typeof(self) __self = self;
[self.pageController setViewControllers:#[[self viewControllerAtIndex:self.currentPageControllerIndex]]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:^(BOOL finished) {
__self.currentPageControllerIndex++;
__self.buttonNext.enabled = YES;
}];
}
#pragma mark - UIPageViewControllerDelegate & DataSource
- (DataChildViewController *)viewControllerAtIndex:(NSUInteger)index
{
DataChildViewController *childViewController = [[DataChildViewController alloc] initWithNibName:#"DataChildViewController" bundle:nil];
childViewController.index = index;
return childViewController;
}
So, I've figured out the problem.
On viewDidLoad I had this line:
self.pageController.view.frame = self.view.bounds;
When it actually should be:
self.pageController.view.frame = self.pageControllerParent.bounds;

Using UIPageViewController with Multiple Views

I have the following code to create a UIPageViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
self.index = 0;
self.pageContainer = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageContainer.dataSource = self;
[[self.pageContainer view] setFrame:[[self view] bounds]];
DMSignUp1ViewController *first = [[DMSignUp1ViewController alloc] init];
DMSignUp2ViewController *second = [[DMSignUp2ViewController alloc] init];
self.viewControllers = [[NSArray alloc] initWithObjects:first, second, nil];
[self.pageContainer setViewControllers:self.viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageContainer];
[[self view] addSubview:[self.pageContainer view]];
[self.pageContainer didMoveToParentViewController:self];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if(self.index == 0) {
return nil;
}
self.index--;
return [self.viewControllers objectAtIndex:self.index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if(self.index == 1) {
return nil;
}
self.index++;
return [self.viewControllers objectAtIndex:self.index];
}
I have two UIViewControllers: DMSignUp1 and 2 which I create and then place in the viewControllers array.
When I go to run it, I get the following error: 'The number of view controllers provided (2) doesn't match the number required (1) for the requested transition'
Which I understand is because I am passing the array to use as the view controller. So how do I set that I want to use the first view controller in the array?
Add the following method at the end of the ViewController.m file
- (DMSignUp1ViewController *)viewControllerAtIndex:(NSUInteger)index {
DMSignUp1ViewController *first= [[DMSignUp1ViewController alloc] initWithNibName:#"APPChildViewController" bundle:nil];
first.index = index;
return childViewController;
}
And, replace the following code
DMSignUp1ViewController *first = [[DMSignUp1ViewController alloc] init];
DMSignUp2ViewController *second = [[DMSignUp2ViewController alloc] init];
self.viewControllers = [[NSArray alloc] initWithObjects:first, second, nil];
With
APPChildViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
Hope this might help you...
If you have the UIPageViewController spineLocation set to anything other than UIPageViewControllerSpineLocationMid when you're using [self.pageContainer setViewControllers: you can only pass an array with one view controller. The API isn't very clear really with it saying Controllers. You then provide the additional viewControllers when the UIPageViewController requests them through the dataSource.
Link to the spine reference.

Resources