Odd UINavigationController and UIPopOverController behavior - uinavigationcontroller

I am presenting a popover with my viewcontroller:
UIBarButtonItem *barButton = (UIBarButtonItem*)sender;
SettingsViewController *settingsViewController = [[SettingsViewController alloc] initWithNibName:nil bundle:nil];
[self presentPopoverFromBarButtonItem:barButton andController:settingsViewController andSize:CGSizeMake(RECT_POPOVER_SETTINGS.size.width, RECT_POPOVER_SETTINGS.size.height)];
and inside this view controller I have a button that pushes another view controller.
AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
Now in this view controller I have an action, and when it's done I do:
[self.navigationController popViewControllerAnimated:YES];
Now the strange behavior is that when I pop the view controller, the
viewWillAppear
method gets called in SettingsViewController, then the breakpoints come back to pop method inside my anotherViewController.
This is a universal App so I am using this same setup on the iPhone, but without the initial UIPopOverController. This is functioning properly in the iPhone (with no popovers, pop method gets called first, then viewWillAppear), but on the iPad the viewWillAppear gets called first, then pop.
So my question is, can somebody explain why this is happening?
Thanks.

Related

Can't access detailViewController from UINavigationController

I'm trying to get a reference to my detailViewController in application didFinishLaunchingWithOptions: method. But here I am faced with a problem.
I have next Views structure is storyBoard (for iPad):
UISplitViewController --> UINavigationController --> UITableViewController --> detailViewController
I created segue with "push" style between UITableViewControllerCell and detailViewController
Screenshot: http://picturepush.com/public/13071076
I'm trying to get a reference to detailViewController in this way:
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
DetailViewController *detailViewController = [navigationController.viewControllers lastObject];
But in result I got reference to my UITableViewController, instead of acceptable
DetailViewController. The navigationController.viewControllers count is 1 but there should be 2 (I think so).
I hope for your help.
Since you created a segue with push style, the detailViewController will only be loaded after you tap on a table view cell. It will not be loaded on the app didFinishLaunchingWithOptions method.
I would suggest you try to make the detailViewController as a property in your table view controller.
#property (nonatomic, strong) UIViewController *detailViewController;
Then in your app didFinishLaunchingWithOptions method, you can access the property:
UITableViewController *tableViewController = [navigationController.viewControllers lastObject];
DetailViewController *detailViewController = tableViewController.detailViewController;
But you will need to avoid using perform segue function and push the detail view controller whenever it is needed with the usual pushViewController method.
[self.navigationController pushViewController:self.detailViewController animated:YES];

Nesting UIPageViewController in a UINavigationController shows only one page

I've spend hours on this and can't get it to work. Hope that someone can help me.
I have a UIPageViewController which works perfectly when I add it to my default view when the application starts. This is what I do:
//Step 1
//Instantiate the UIPageViewController.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
//Step 2:
//Assign the delegate and datasource as self.
self.pageViewController.delegate = self;
self.pageViewController.dataSource = self;
//Step 3:
//Set the initial view controllers.
ContentViewController *contentViewController = [[ContentViewController alloc] initWithNibName:#"ContentViewController" bundle:nil];
contentViewController.labelContents = [self.modelArray objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:contentViewController];
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
[self addChildViewController:self.pageViewController];
[self.view addSubview: self.pageViewController.view];
Now I want to use a navigation controller in my initial view which then pushes the UIPageViewController on the stack, when the user clicks on a button. This works too, but when I'm in landscape mode the UIPageViewController shows only one page (which is horizontally streched) and not two, as it should be. I need to rotate the device to portrait and then back to landscape mode to force the UIPageViewController to show both pages.
I'm adding the UIPageViewController to the navigation controller with:
[self.navController pushViewController:self.pageViewController animated:NO];
When i debug my code I see that without the navigation controller the delegate methods of the UIPageViewController are called on startup. When I push the UIPageViewController on the navigation controller they're not called until I rotate the device.
Does anyone know how to solve this? Thanks in advance for any help/tips on this.
Figured this out with help from this thread.
The key is setting the spineLocation on the pageViewController when you create it through the options dictionary:
options = [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMid]
forKey: UIPageViewControllerOptionSpineLocationKey];
And this option should be put inside of a check to see what the current interface orientation is.
In the above mentioned thread, the interface orientation is checked by a ternary operator; I put it in an if-statement so that I can use the same opportunity to tack an extra UIViewController onto the viewControllers array.
If the interface orientation is portrait, 'options' will still be nil when the pageViewController is created, and so it'll go on to create it in portrait mode with only one viewController.
Here's my whole viewDidLoad code in BookViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
_modelController = [[ModelController alloc] init];
DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0
storyboard:self.storyboard];
NSMutableArray *viewControllers = [NSMutableArray arrayWithObjects:
startingViewController,
nil];
NSDictionary *options;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
DataViewController *secondViewController = [self.modelController viewControllerAtIndex:1
storyboard:self.storyboard];
[viewControllers addObject:secondViewController];
options = [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMid]
forKey: UIPageViewControllerOptionSpineLocationKey];
}
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:options];
self.pageViewController.delegate = self;
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds
self.pageViewController.view.frame = self.view.bounds;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
}

Refreshing tabbarcontroller views on logout in iPhone application

We have a tab bar + navigation control project. We have created a tab bar through interface builder and added all tab items to the tab bar controller through through interface builder.
Our application has a login screen which we are showing as a modal form at the beginning. Once the user is authenticated, the login screen is dismissed (using dismissModalScreen) and the MainWindow is shown. This is all working fine.
Now, we have a logout option clicking on which we should show the login screen back. We are able to show that as well. But the issue is once the user is authenticated again, we want all the screens to be loaded freshly. Currently, all screens maintain their state and retain previous data.
What we have tried to do is:
[AppDelegate tabBarController release];
AppDelegate.tabBarController = nil;
We have tried releasing the tabBarController and setting it to nil. But once the view is shown, we are not able to select any of the tabs and the view is blank.
What we need is:
Once the user logs in second time, how to release and reload each viewcontroller which are part of tabbar.
Your help will be greatly appreciated.
We have found a way out. What we are doing now is, once the user logs back in, we are releasing the view controllers and recreating them and setting it back to the uitabbar.
NSMutableArray *arrControllers = [[AppDelegate.tabBarController.viewControllers] mutablecopy];
[arrControllers removeAtIndex:3];
[arrControllers removeAtIndex:2];
----Create New Controllers
UIViewController viewController2 = [[UIViewController alloc] initWithNibName:#"viewController2" bundle:nil] autorelease];
UIViewController viewController3 = [[UIViewController alloc] initWithNibName:#"viewController3" bundle:nil] autorelease];
--Create Nav controller
UINavigationController navViewController2 = [[[UINavigationController alloc] initWithRootViewController:viewController2 ] autorelease];
UINavigationController navViewController3 = [[[UINavigationController alloc] initWithRootViewController:viewController3 ] autorelease];
[arrControllers addObject:navViewController2];
[arrControllers addObject:navViewController3];
[AppDelegate.tabViewController setViewControllers:arrControllers];
It is working fine after this change.
Only problem we faced was if we release all objects from the array, we get a carsh. Is it because, when you release allObjects, the first controller which is the RootViewController is getting released?
Thanks

How do I push UIVIewController on modalViewController?

I have tabbar + navigation app. I call filterModalController:
[self.navigationController presentModalViewController:filterModalController animated:YES];
It appeared OK. Then I want to push officeController.
[self.navigationController pushViewController:officeController animated:YES];
But officeController didn't appeared. What is wrong?
My understanding is that modal view controllers are not meant to push other view controllers, so you may want to think about the hierarchy of views you are using. Modals are meant to return to the parent view controller. You may need to call
[self.navigationController dismissModalViewControllerAnimated:YES];
before you push the officeViewController.
When I ran into this, I ended up pushing a Navigation controller as the modal view controller, with the 'filterModalController' as the root view controller. Since you already have a navigation controller, this may make things too confusing.
UINavigationController* filterNavController = [[UINavigationController alloc]
initWithRootViewController:filterModalController];
// Display the nav controller modally.
[self presentModalViewController:filterNavController animated:YES];
[locationNavController release];
Then the filterNavController can push your officeController.

popToRootViewControllerAnimated toolbar not correctly updating

I have a UITabBarController whose tabs are UINavigationController. I have initialized each navigationController by:
iPhoneApp *appDelegate = (iPhoneApp *)[[UIApplication sharedApplication] delegate];
[appDelegate.navigationController2 initWithRootViewController:countryController];
I then drill down the navigationController with:
[self.navigationController pushViewController:myViewController animated:YES];
I want to pop to the root of my navigationController(s) when the user clicks on a tabBar tab.
[delegate.navigationController2 popToRootViewControllerAnimated:NO];
This seems to work great when I only pushViewController one level, but fails when I drill down multiple levels in my navigationController. What happens is that it pops to root but doesn't contain my Root's backbutton or title:
self.navigationItem.title = #"My title";
self.navigationItem.hidesBackButton = YES;
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStyleDone
target:self
action:#selector(handleBack:)];
if I don't poptoroot and just use the navigationControlls back button, everything works correctly.
I logged my viewControllers before and after i poptoroot
before:
MainCountryController: 0x3d53650,
IndividualCountryMfgViewController: 0x3d67d50,
IndividualCountryProductViewController: 0x3d60870
after:
MainCountryController: 0x3d53650
it's the right view, but wrong toolbar title and backbutton.
Anyone have any ideas? Thanks for your help.
I got this working by disconnecting my navigation controllers from IB an just creating them programatically.

Resources