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.
Related
I'm following these instructions per the ios docs on UIPageViewController and UIPageControl:
A page indicator will be visible if both methods are implemented, transition style is 'UIPageViewControllerTransitionStyleScroll', and navigation orientation is 'UIPageViewControllerNavigationOrientationHorizontal'. Both methods are called in response to a 'setViewControllers:...' call, but the presentation index is updated automatically in the case of gesture-driven navigation.
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
So I have implemented them as follows, inside a UIViewController wrapper class:
Here is the initialization:
#interface PageViewWrapperViewController : UIViewController <UIPageViewControllerDelegate, UIPageViewControllerDataSource>
#property (strong, nonatomic) UIPageViewController *pageController;
#property (assign, nonatomic) NSInteger index;
#property (strong, nonatomic) UILabel *screenNumber;
#property UIPageControl *childPageControl;
#end
- (void)viewDidLoad {
[super viewDidLoad];
self.index = 0;
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
self.pageController.delegate = self;
[self.pageController.view setFrame:[[self view] bounds]] ;
UIViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
//[self.pageController didMoveToParentViewController: self];
self.childPageControl = [[UIPageControl alloc] init];
// self.childPageControl = [self getPageControlForPageViewController:self.pageController];
self.childPageControl.alpha = .5;
[self.view bringSubviewToFront:self.childPageControl];
}
Here are the required methods for the UIPageControl:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return 5;
}
-(NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return self.index;
}
And here is where I give the PageController the individual view controllers:
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index{
if(index==0)
{
//JudgeViewController *next1 = [[JudgeViewController alloc] init];
JudgeTutorViewController *next1 = [[JudgeTutorViewController alloc] init];
next1.view.userInteractionEnabled = NO;
return next1;
}
else if(index==1)
{
FollowTableViewController *next1 = [[FollowTableViewController alloc] init];
next1.view.userInteractionEnabled = NO;
next1.typeDisplay = 1;
return next1;
}
else if (index==2)
{
StatTutorViewController *next1 = [[StatTutorViewController
alloc] init];
next1.view.userInteractionEnabled = NO;
next1.user = [[UserObject userUnique] copyUser].name;
return next1;
}
else if (index==3)
{
MenuTutorViewController *next1 = [[MenuTutorViewController
alloc] init];
next1.view.userInteractionEnabled = NO;
return next1;
}
else if(index ==4)
{
BuzzTutorViewController *next1 = [[BuzzTutorViewController alloc] init];
next1.view.userInteractionEnabled = NO;
return next1;
}
self.index = index;
return nil;
}
The UIPageViewController itself is working fine, but I don't see the UIPageControl. What is missing from my code?
Thank you for any suggestions.
Actually, neither of these answers is what the problem is. In fact, I didn't change my code but once I flushed my build settings a few times that did the trick. Thank you all!
self.childPageControl = [[UIPageControl alloc] init];
// self.childPageControl = [self getPageControlForPageViewController:self.pageController];
self.childPageControl.alpha = .5;
[self.view bringSubviewToFront:self.childPageControl];
At no point are you adding the page control as a subview. The view debugger in Xcode is a great way to spot missing UI elements like this.
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 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;
I'm wondering how I would be able to design my own custom tabs, that when clicked could show mutiple view on the screen
http://tinypic.com/r/2cxtjk7/5
I'm only currently able to find the bottom tabbed bar approach. Thanks for your help im quite new still so if you could explain in detail that would be great :)
Show Multiple views using custom tabs
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
{
AccountViewController *accountViewController = [[AccountViewController alloc]
initWithNibName:#"AccountViewController" bundle:nil];
MoreViewController *moreViewController = [[MoreViewController alloc]
initWithNibName:#"MoreViewController" bundle:nil];
BarTabViewController *barTabViewController = [[BarTabViewController alloc]
initWithNibName:#"BarTabViewController" bundle:nil];
LocationsViewController *locationsViewController = [[LocationsViewController alloc]
initWithNibName:#"LocationsViewController" bundle:nil];
self.viewControllers = [NSArray arrayWithObjects:locationsViewController, accountViewController,
barTabViewController, moreViewController, nil];
[self.view addSubview:locationsViewController.view];
self.selectedController = locationsViewController;
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.selectedIndex = 0;
self.tabBarController.viewControllers = [NSArray arrayWithObjects:locationsViewController, accountViewController,
barTabViewController, moreViewController, nil];
self.tabBarController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self.navigationController pushViewController:delegate.tabBarController animated:YES];
return self;
}
Like I said, this will display the selected controller properly, however when the app launches and I try to switch views with the tab bar, the subview just become grey... The following is the code to switch items:
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
if (item == locationsTabBarItem) {
UIViewController *locationsController = [viewControllers objectAtIndex:0];
[self.selectedController.view removeFromSuperview];
[self.view addSubview:locationsController.view];
self.selectedController = locationsController;
}
else if (item == accountsTabBarItem) {
UIViewController *accountsController = [viewControllers objectAtIndex:1];
[self.selectedController.view removeFromSuperview];
[self.view addSubview:accountsController.view];
self.selectedController = accountsController;
}
else if (item == barTabTabBarItem) {
UIViewController *barTabController = [viewControllers objectAtIndex:2];
[self.selectedController.view removeFromSuperview];
[self.view addSubview:barTabController.view];
self.selectedController = barTabController;
}
else {
UIViewController *moreController = [viewControllers objectAtIndex:3];
[self.selectedController.view removeFromSuperview];
[self.view addSubview:moreController.view];
self.selectedController = moreController;
}
}
I added thing in view did load in the end here:
PatientDetailsViewController *patientDetailsView =
[[PatientDetailsViewController alloc]
initWithNibName:#"PatientDetailsViewController" bundle:nil];
[self addChildViewController:patientDetailsView];
[self.patientDetailsView addSubview:patientDetailsView.view];
[self.patientDetailsView setClipsToBounds:YES];
and then animated its opening with a button by adjusted the view frame size with an animation.
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];