I use a UIPageViewController for two view controllers. In the first view controller I have a 'NEXT' button to go to the second view controller. My question is: How can I wire it up?
In my first and second view controllers I do not have code yet. However, in my RootViewController I have this code:
- (void)viewDidLoad {
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController2"];
self.pageViewController.dataSource = self;
SeekerRegistrasiViewController *tvc = [self.storyboard instantiateViewControllerWithIdentifier:#"seeker1"];
Seeker2RegistrasiViewController *pvc;
self.array =[NSArray arrayWithObjects:tvc, pvc, nil];
[self.pageViewController setViewControllers:self.array direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height -60);
self.pageViewController.view.backgroundColor = [UIColor clearColor];
[self addChildViewController:pageViewController];
[self.view addSubview:pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController{
if ([viewController isKindOfClass:[Seeker2RegistrasiViewController class]]) {
SeekerRegistrasiViewController *pvc = [self.storyboard instantiateViewControllerWithIdentifier:#"seeker1"];
return pvc;
}
else return nil;
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController{
if ([viewController isKindOfClass:[SeekerRegistrasiViewController class]]) {
Seeker2RegistrasiViewController *tvc = [self.storyboard instantiateViewControllerWithIdentifier:#"seeker2"];
return tvc;
}
else return nil;
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
return 2;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
return 0;
}
I would appreciate any help that would guide me in the right direction.
You can do it using setViewControllers:direction:animated:completion:.
Look at this answer: Is it possible to Turn page programmatically in UIPageViewController?
You basically set again all the view controllers (already switched), define a direction (which the animation will use, for example, UIPageViewControllerNavigationDirectionForward), use YES for animated and a completion block for processing afterwards (like updating the currentPage index you manage).
Related
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.
When a user presses the center UITabBarItem I present a modal UIView. Think of it like Instagram.
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController{
if(viewController == [self.viewControllers objectAtIndex:2])
{
CameraViewController *cameraVC = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"cameraVC"];
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:cameraVC];
navController.navigationBar.barStyle = UIStatusBarStyleLightContent;
[self presentViewController:navController animated:YES completion:nil];
return NO;
}
else
{
return YES;
}
}
This works perfectly.
When I'm done taking a picture in CameraViewController I want the view to be dismissed and the 4th UITabBarItem to be selected for the results of the picture (HistoryViewController).
This is how I do that in CameraViewController (who is modally pushed):
[self dismissViewControllerAnimated:YES completion:nil];
[(UITabBarController *)self.presentingViewController setSelectedIndex:3];
And this is where it gets buggy.
As you can see the text in the 4th tab is selected, but the first tab icon is still selected. Also the presented view is the one from the first tab.
After 10 seconds or so it eventually changes the view to the, correct, 4th tab.
I'm trying to find out what process creates this slowdown so I've set up a lot of NSLog's.
The approximate 10 second slowdown is between [(UITabBarController *)self.presentingViewController setSelectedIndex:3]; in the CameraViewController and viewDidLoad in HistoryViewController.
What is happening in between these calls/methods that could cause the slowdown?
Edit:
In CameraViewController:
- (void)scan {
dispatch_queue_t scanTesseract = dispatch_queue_create("scanTesseract", NULL);
dispatch_async(scanTesseract, ^(void) {
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD setForegroundColor:[UIColor ht_mintDarkColor]];
[SVProgressHUD showProgress:0 status:#"Scanning"];
});
//background processing goes here
[self.tesseract setImage:self.imgToScan.blackAndWhite];
[self.tesseract recognize];
[self filterResults:[self.tesseract recognizedText]];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
[self scanningDone];
});
}
- (void)scanningDone {
[LastScan getInstance].hasBeenViewed = FALSE;
[self dismissViewControllerAnimated:YES completion:nil];
[(UITabBarController *)self.presentingViewController setSelectedIndex:3];
}
In HistoryViewController:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"ViewDidLoad");
self.navigationController.navigationBar.barStyle = UIStatusBarStyleLightContent;
self.collectionView.backgroundColor = [UIColor whiteColor];
}
You are calling your scanningDone from within a background queue. Execute that method on the main Queue.
dispatch_async(dispatch_get_main_queue(), ^{
[self scanningDone];
});
What about this?
[self dismissViewControllerAnimated:YES completion:^{
[(UITabBarController *)self.presentingViewController setSelectedIndex:3];
}];
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.
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.
I am new to iOS and I am using UIPageViewController. Here is my code:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
firstVC *first1 = [[firstVC alloc] initWithNibName:#"firstVC" bundle:nil]; return first1;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
secondVC *second1 = [[secondVC alloc] initWithNibName:#"secondVC" bundle:nil]; return second1;
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { NSArray *viewControllers = nil; viewControllers = [NSArray arrayWithObjects:first,second, nil]; [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerSpineLocationMid animated:NO completion:NULL];
}
-(UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
NSArray *viewControllers = nil; viewControllers = [NSArray arrayWithObjects:second, first, nil]; [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerSpineLocationMid animated:YES completion:NULL]; return UIPageViewControllerSpineLocationMid;
}
- (void)viewDidLoad { [super viewDidLoad]; [self.navigationController setNavigationBarHidden:NO]; self.navigationController.navigationBar.tintColor = [UIColor blackColor]; self.title = #"DEMO"; NSDictionary *options = [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin] forKey: UIPageViewControllerOptionSpineLocationKey]; self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerSpineLocationMid options:options]; self.pageViewController.delegate = self; self.pageViewController.dataSource = self; first = [[firstVC alloc] initWithNibName:#"firstVC" bundle:nil]; second = [[secondVC alloc] initWithNibName:#"secondVC" bundle:nil]; NSArray *viewControllers = [NSArray arrayWithObjects:first,second,nil]; [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerSpineLocationMid animated:NO completion:nil]; [self addChildViewController:self.pageViewController]; [self.view addSubview:self.pageViewController.view]; self.modalPresentationStyle=UIModalPresentationCurrentContext; [self.pageViewController didMoveToParentViewController:self]; CGRect pageViewRect = self.view.bounds; pageViewRect = CGRectInset(pageViewRect, 20.0, 20.0); self.pageViewController.view.frame = pageViewRect; self.view.gestureRecognizers = self.pageViewController.gestureRecognizers; self.pageViewController.doubleSided=YES; }
It is loading double sided pages in both orientations but a problem arises when I swipe the page and only one view is shown. I want a page-like animation. Please help. Thanks in advance for help and support.
where do you initialize your UIPageViewController?
at the point of initializing you MUST define the transition style as it's a readonly property.
The code should look like this;
self.pageViewController = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:nil];