I have a slider in Root UIPageViewController which need to pass the slider value to the child View Controller. Which works for other child view controller except than the one already loaded !
I have Slider in RootView for Slider Value Change I have called -
- (void)sliderValueChanged:(UISlider *)slider {
fontValue = slider.value;
DetailsVC *dataViewController = [self viewControllerAtIndex:self.newsIndex storyboard:self.storyboard];
[dataViewController refreshViewForFontSize:slider.value];
}
I have usual Methods of UIPageViewController which works fine. Just the problem is it works only after the ViewDid Load method is called in child ViewControllers
- (DetailsVC *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard {
// Return the data view controller for the given index.
if (([self.pageContent count] == 0) || (index >= [self.pageContent count])) {
return nil;
}
DetailsVC *dataViewController = [[DetailsVC alloc] init];
dataViewController.fontValue = fontValue;
return dataViewController;
}
Finally found the solution
DetailsVC *dataViewController = [self viewControllerAtIndex:currentIndex-1 storyboard:self.storyboard];
dataViewController.textColor = textColor;
NSArray *viewControllers = #[dataViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
The currect way to get reload the child viewcontroller is to get it via
setViewControllers: direction: animated: completion:
If anyone need a example project I have upload it at
https://github.com/bishalg/BGPageViewController
Related
I am debugging legacy code which is always fun. The old code tried to mock the splitView delegate methods, causing all sorts of issues - mainly crashing: on a Plus device in Portrait, rotating to landscape caused the crash - if there was no detail view set, old code attempted to create one in a dodgy hack and it was just useless...
My app is UISplitViewController based, where I have a navigation stack in both master and detail sides of the splitView.
By reading though SO and using this example and was able to implement UISplitViewController delegate methods and everything is working correctly in regards to rotation, and showing the correct master/detail views when appropriate. Here is my implementation: (apologies for wall of code snippets)
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
if ([secondaryViewController isKindOfClass:[UINavigationController class]]
&& [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[AECourseHTMLTableViewController class]]
&& ([(AECourseHTMLTableViewController *)[(UINavigationController *)secondaryViewController topViewController] htmlContentEntry] == nil)) {
// If the detail controller doesn't have an item, display the primary view controller instead
return YES;
}
return NO;
}
And the other splitView delegate method - see comments in code for where I'm stuck.
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController {
// If detail view already exists
if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
if ([controller isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)controller visibleViewController] isKindOfClass:[AECourseHTMLTableViewController class]]) {
return controller;
}
}
}
// Create detail view
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"CourseHTMLNav"];
if ([navController.viewControllers.firstObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
AECourseHTMLTableViewController *courseViewController = navController.viewControllers.firstObject;
[self configureViewController:courseViewController entry:self.contentSection.sections[0] indexPath:courseViewController.currentIndexPath];
}
// Enable back button
UIViewController *controller = [navController visibleViewController];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
if (!self.splitViewController.isCollapsed) {
UINavigationController *navController = self.splitViewController.viewControllers.firstObject;
AEContentMenuTableViewController *contentMenuVC = navController.viewControllers.firstObject; // This controller needs to be master in Landscape
NSMutableArray<UIViewController *> *controllers = [navController.viewControllers mutableCopy]; // Contains 3 controllers, first needs removed
NSMutableArray *toDelete = [NSMutableArray new];
for (UIViewController *viewController in controllers)
if ([viewController isKindOfClass:[contentMenuVC class]] || [viewController isKindOfClass:[AECourseHTMLTableViewController class]]) {
[toDelete addObject:viewController]; // Remove first VC, so master should become AEContentMenuVC?
break;
}
// Remove the object
[controllers removeObjectsInArray:toDelete];
// Set viewControllers
navController.viewControllers = controllers;
}
return navController;
}
AECourseHTMLTableViewController has next/prev buttons to select the next row in the tableview of the tableview menu class class (AEContentMenuTableViewController). I have a delegate function which can tell me the current indexPath in which AECourseHTML... is using from AEContentMenu..., and when calling it, it selects the menu tableview row and instantiates a new AECourseHTML... and pushes it.
This is where I'm stuck. In Portrait, pressing next/prev is fine, it selects the correct row and works as expected. But once I rotate the device, both master and detail views show the detail view. I can press "Back" on the master view, and it takes me to the correct AEContentMenu... class. As noted in the code snippet comments, I need to remove a ViewController from the master stack (the first object actually), and AEContentMenu... should become the first object of that stack - so when rotating, that should be the master view.
Apologies for such a long post, I've been banging my head with this for weeks now and I want to include as much info as possible in this question. Thanks in advance.
I found a solution which works well for my use cases. It may not be the cleanest code, but I'm happy with what I've got.
splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
remains unchanged. I have updated my splitViewController:separateSecondaryViewControllerFromPrimaryViewController: delegate method with the solution. Any feedback is welcome.
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController {
// If detail view already exists
if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
if ([controller isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)controller visibleViewController] isKindOfClass:[AECourseHTMLTableViewController class]]) {
return controller;
}
}
}
// Return CourseVC
UINavigationController *navController = splitViewController.viewControllers.firstObject;
UIViewController *viewController;
for (viewController in navController.viewControllers) {
if ([navController.viewControllers.lastObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
return viewController;
} else {
// Create detail view
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"CourseHTMLNav"];
if ([navController.viewControllers.firstObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
// Enable back button
UIViewController *controller = [navController visibleViewController];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
AECourseHTMLTableViewController *courseViewController = navController.viewControllers.firstObject;
// If next/prev has been tapped, configure current ContentHTML
if (self.currentContentHTML) {
[self configureViewController:courseViewController entry:self.currentContentHTML indexPath:courseViewController.currentIndexPath];
} else {
// Create new ContentHTML from first row of AEContentMenuVC
[self configureViewController:courseViewController entry:self.contentSection.sections[0] indexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}
return navController;
}
}
}
return navController;
}
Your top if statement should return nil. Since you were returning the nested navigation controller you were missing out on the default behaviour of popping the master navigation's top controller which is required so it can then be placed on the right.
The default behaviour will find that nested nav controller and pop it. However the reason you still need to search for it yourself is if it isn't there then you need to load the detail nav from the storyboard as you have done.
Now I am creating app tour for my app by using uipageviewcontroller, its working fine when I am swipe the pages in view controller but what I need was the page will move when I click the button
below is my button click action here i can able to put only custom page index but I need to give the current page of the index
- (IBAction)donebtnaction:(id)sender {
NSLog(#"current page: %#",indexpathpage);
PageContent *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.PageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
and i am also tried to save the index when swipe after and swipe before,
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController{
NSUInteger index = ((PageContent*) viewController).pageIndex;
if (index == NSNotFound){
return nil;
}
index++;
[self.pagecontrol setCurrentPage:index];
if (index == [self.arrPageTitles count])
{
return nil;
}
return [self viewControllerAtIndex:index];
}
Please give me solution for this .
Try navigating the page by pop and push actions . That will help to which action u are performing and which indexPath its heading .
For example for push which is inserting from stack
xController *xVC = [[xController alloc]init];
xVC.view.backgroundColor = [UIColor whiteColor];
[self.navigationController pushViewController:xVC animated:YES];}
for example to pop which means taking it out from the stack
[self.navigationController popViewControllerAnimated:YES];
I'm currently using the ECSlidingViewControllersample and I'm having a small bug while opening the same view I'm currently in.
For Example if I'm inside GudjonDaneil and I open the "drawer" and hit GudjonDaniel again the viewDidLoad method calls twice, If I open it again it calls the viewDidLoad 3 times in a row. It seems like the views are stacking above each other. I need someway of killing the class(view) once I open the view again.
Here's what happens once you click on the -UItableView
NSLog(#"Channel 1 Loading");
tableViewCellLoading = true;
[self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"firstChannel"];
self.slidingViewController.topViewController.view.frame = frame;
[self.slidingViewController resetTopView];
[tableView deselectRowAtIndexPath:indexPath animated:YES]; }];
How the drawer application looks like
http://gyazo.com/f811283fadbad5b50ee4d594b6798f18
I know it's been a while since this question was asked... but here's an answer for anyone else that is looking.
The following is from my "MenuController.m" file.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.slidingViewController.topViewController.view.layer.transform = CATransform3DMakeScale(1, 1, 1);
UINavigationController *currentNav = (UINavigationController *)self.slidingViewController.topViewController;
UIViewController *theControllerYouWant = [currentNav.viewControllers objectAtIndex:([currentNav.viewControllers count]-1)];
NSString *CurrentSelectedCViewController = NSStringFromClass(theControllerYouWant.class);
if (indexPath.row == 1 && ![CurrentSelectedCViewController isEqualToString:#"PeopleTableViewController"]) {
[(UINavigationController *)self.slidingViewController.topViewController setViewControllers: [NSArray arrayWithObject: [self.storyboard instantiateViewControllerWithIdentifier:#"PeopleView"]]];
} else if (indexPath.row == 2 && ![CurrentSelectedCViewController isEqualToString:#"CompanyPickerController"]) {
[(UINavigationController *)self.slidingViewController.topViewController setViewControllers: [NSArray arrayWithObject: [self.storyboard instantiateViewControllerWithIdentifier:#"CompanyView"]]];
}
[self.slidingViewController resetTopViewAnimated:YES];
}
The basic explanation is this:
Get a reference to the current Navigation Controller
Get a reference to the Navigation Controller's top view (always the last view in its
array)
Get a string representation of that view.
Depending on which menu cell you clicked on, check if the slidingViewController's
current top view is the same that you are going to..
If it's not the same, load the proper view in to topviewcontroller
Close the menu
I have an app with a storyboard. I am using several View Controller with segues to choose from a table view depending on the item selected. I want to have a page view controller in which the pages will be one or more view controller from the storyboard, even repeating them. I am trying to init the Page View Controller like this:
....
self.dataSource=self;
UIViewController *initialViewController =[self viewControllerAtIndex:current];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self setViewControllers:viewControllers direction:UIPageViewControllerNavigationOrientationHorizontal animated:NO completion:nil];
....
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index
{
if ((descriptions.count == 0) ||
(index >= descriptions.count)) {
return nil;
}
current=index;
// Create a new view controller and pass suitable data.
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
Description *description=[DBCompany getDescriptionById:language descriptionId:[[descriptions objectAtIndex:index] integerValue]];
UIViewController *viewController=nil;
if(description.frame==100){
viewController=[[Company100ViewController alloc] init];
((Company100ViewController*)viewController).companyId = companyId;
}
return viewController;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ((descriptions.count == 0) ||
(current-1 < 0)) {
return nil;
}
return [self viewControllerAtIndex:current-1];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ((descriptions.count == 0) ||
(current >= descriptions.count)) {
return nil;
}
return [self viewControllerAtIndex:current+1];
}
However the viewcontroller appears in black. I think is because I am adding the UIviewcontroller class but not connecting it to any view, XIB, in my case with the storyboard.
How can I use the different view controller from the storyboard programmatically to use in a page view controller?
If you do viewController=[[Company100ViewController alloc] init] then yes this not a controller that is associated to a storyboard or an XIB. To load the controller with an XIB you have to do the following:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
Company100ViewController * vc = (Company100ViewController *)[sb instantiateViewControllerWithIdentifier:#"vc-identifier"];
In the storyboard make sure you set the view controller's id to vc-identifier; whatever identifier you choose.
Swift 3
let sb = UIStoryboard(name: "MainStoryboard", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "vc-identifier")
Swift
In case you want to init from a UIViewController object.
guard let vc = storyboard?.instantiateViewController(withIdentifier: "nameVC") else {
return
}
// add to current view
view.addSubview(vc.view)
I'm using UIPageViewController and story board in my application.
When in portrait, i'm using following code to jump from one page to another page and its working fine.
int direction = UIPageViewControllerNavigationDirectionForward;
if ([self.modelController currentPage] < pagenum)
{
direction = UIPageViewControllerNavigationDirectionForward;
}
else if ([self.modelController currentPage] > pagenum)
{
direction = UIPageViewControllerNavigationDirectionReverse;
}
[self.pageViewController setViewControllers:[NSArray arrayWithObject:[self.modelController viewControllerAtIndex:pagenum storyboard:self.storyboard]] direction:direction animated:YES completion:NULL];
But the same code is not working when we are in landscape mode. How to turn the pages when in landscape?
If you look at the Page-Based Application Template in Xcode you find the following UIPageViewControllerDelegate method:
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
if (UIInterfaceOrientationIsPortrait(orientation)) {
// In portrait orientation: Set the spine position to "min" and the page view controller's view controllers array to contain just one view controller. Setting the spine position to 'UIPageViewControllerSpineLocationMid' in landscape orientation sets the doubleSided property to YES, so set it to NO here.
UIViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
self.pageViewController.doubleSided = NO;
return UIPageViewControllerSpineLocationMin;
}
// In landscape orientation: Set set the spine location to "mid" and the page view controller's view controllers array to contain two view controllers. If the current page is even, set it to contain the current and next view controllers; if it is odd, set the array to contain the previous and current view controllers.
DataViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSArray *viewControllers = nil;
NSUInteger indexOfCurrentViewController = [self.modelController indexOfViewController:currentViewController];
if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
UIViewController *nextViewController = [self.modelController pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
viewControllers = [NSArray arrayWithObjects:currentViewController, nextViewController, nil];
} else {
UIViewController *previousViewController = [self.modelController pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
viewControllers = [NSArray arrayWithObjects:previousViewController, currentViewController, nil];
}
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
return UIPageViewControllerSpineLocationMid;
}
When returning UIPageViewControllerSpineLocationMid from the above method, you are telling the UIPageViewController that it should expect to display two UIViewControllers side by side (i.e. two pages).
The key here is that you must pass the correct number of UIViewControllers when calling the UIPageViewController's setViewControllers:direction:animated:completion: method.
Showing two pages? Pass two UIViewControllers.
Showing one page? Pass one UIViewController.
The code you presented will never pass more than one UIViewController to the UIPageViewController.
If the UIPageViewControllerSpineLocation does not correspond with the amount of UIViewControllers you are passing it will crash or just do nothing.
Let me know if you need further help.