View controller hierarchy initialization has changed in iOS5 - uinavigationcontroller

Here is the problem.
I have the following view hierarchy which is pushed modally using a flip animation:
BackViewController
UITabBarController
UINavigationController
ForecastListViewController
It looks like this:
Here is the sequence of calls during the flip in iOS 4.3:
BackViewController viewDidLoad
ForecastListViewController viewDidLoad
BackViewController viewWillAppear
ForecastListViewController viewWillAppear
BackViewController viewDidAppear
ForecastListViewController viewDidAppear
In iOS 5.0 the order is different (step 2 and 3 are switched):
BackViewController viewDidLoad
BackViewController viewWillAppear
ForecastListViewController viewDidLoad
ForecastListViewController viewWillAppear
BackViewController viewDidAppear
ForecastListViewController viewDidAppear
The problem is that now the top of the navigation bar appears 10 pixels under the bottom of the status bar during the flip animation, then snaps back to its expected position once the animation terminates.
I have tried changing the navigation bar frame in ForecastListViewController viewDidLoad method without success: nothing happens.
If I force a load of the ForecastListViewController so it occurs as as step 2 (by forcing a traversal of the hierarchy while investigating this issue) all is fine again. But I am looking for a good solution instead of this "hack".
Any one with this problem?
Any clue or pointer?
I am at a loss on how to solve this elegantly

Related

UITabBarController in UISplitViewController with Storyboard

I have UISplitViewController with UITabBarController as its master. UITabBarController contains one UINavigationController with UITableViewController as its root (it is main menu of my app).
After tapping on any cell in main menu, in UISplitViewController's detail part another UITableViewController should be presented (let's call it detail view).
In landscape mode everything works OK.
But in portrait, whet I tap on cell in main menu, the detail view is presented modally, and not pushed, like it supposed to. Also, when rotating from landscape to portrait, the main menu is presented instead of detail view, and after I click on main menu's position to show detail view, it is presented modally with no possibility to rotate or to go back.
Removing UITabBarController and setting UINavigationController as UISplitViewController's master works as I want (in landscape mode we have menu|detail views side by side and in portrait mode controllers behave like they were on regular UINavigationController). But then the UITabBarController is gone.
What I've tried:
every possible segue type - none of them works the way I want
subclassing UIStoryboardSegue to implement custom behavior depending on UISplitViewController's viewControllers param (in portrait mode it has only one view controller - master) - but I couldn't recognise classes (thank you Swift!)
What I want is to do it entirely in Storyboard (OK, custom segues doesn't count) - I want an elegant solution and I refuse to believe it's impossible.
Working on iOS 8 SDK, Xcode 6.2, iPhone 6 Plus
Unfortunately there is no absolutely elegant solution to this one (as far as I've managed to accomplish). Hoping that Apple will eventually sort it out, but in the meantime, this is the nicest way possible:
Place one custom segue instead of Show Detail
In perform method of your custom segue have something like:
- (void)perform
{
MasterViewController *source = self.sourceViewController;
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
UISplitViewController *splitViewController = appDelegate.splitViewController;
if ([splitViewController.viewControllers count] > 1) {
[source performSegueWithIdentifier:#"showDetail" sender:source];
if (appDelegate.masterPopoverController) {
[appDelegate.masterPopoverController dismissPopoverAnimated:YES];
}
} else {
[source performSegueWithIdentifier:#"showDetailSmallDevice" sender:source];
}
}
[splitViewController.viewControllers count] is here just to separate large devices (iPads & iPhone 6 Plus) and the other, smaller ones
In your Storyboard, wire up one segue named showDetail which is actually a showDetail, to the detail navigation controller, and directly to the contents view controller another showDetailSmallDevice which is actually Show
(Push)
See the example:
http://i.stack.imgur.com/GQpg3.png
EDIT: SplitViewController needs two Navigation Controllers. The solution is that you need to insert another Navigation Controller between the SplitViewController and the DatailViewController. Then, from the TableView, preform a Segue directly to the second Navigation Controller. The SplitViewControllers wants two Navigation Controllers...
Maybe a good way could be to start a new SplitViewController project on IB. There are various default methods and properties to manage a SplitViewController. You can find something in appDelegate class, it could be a good starting point.
OLD: I like Mateusz's answer, just a point that is possible to use self.splitViewController.isCollapsed for testing if DetailViewController is or it could be shown on screen. With this property there is no need to count viewControllers.
#property(nonatomic, readonly, getter=isCollapsed) BOOL collapsed
From documentation: A Boolean value indicating whether only one of the child view controllers is displayed. This property is set to YES when the split view controller content is semantically collapsed into a single container. Collapsing happens when the split view controller transitions from a horizontally regular to a horizontally compact environment. After it has been collapsed, the split view controller reports having only one child view controller in its viewControllers property.

UISplitViewController - Unbalanced calls to begin/end appearance transitions

I am using the new UISplitViewController for ios8 that has preferred display modes. My master split view is a UITabBarController with a UINavigationController inside it. I disabled the presentsWithSwipe gesture, and instead have a button in my detail view controller that toggles the splitviewcontroller between 2 preferred display mode states: UISplitViewControllerDisplayModeAllVisible and UISplitViewControllerDisplayModePrimaryHidden.
When I toggle the button so that both view controllers are visible, I can see my navigation controller display it's tableview controller. However, when I click on a cell in my tableviewcontroller that performs a storyboard segue to another view controller, I get the following error:
Unbalanced calls to begin/end appearance transitions for <ReferenceTableViewController: 0x7fde50540d50>
My set up is
SplitMasterViewController --> UITabBarController --> UINavigationController --> ReferenceTableViewController --> ReferenceDetailViewController
SplitDetailViewController --> UINavigationController --> MainViewController
Basically, I want to animate in/out the master view controller which provides information relevant to the MainViewController. Actions taken in the MasterViewController do not replace out the DetailViewController, but rather should navigate within the MasterViewController frame. Kind of like having an iPhone side by side with a smaller iPad, with independent content in each.
However, I'm not getting any of the view appearance methods being called in the ReferenceTableViewController or the RefernenceDetailViewController. I always call super if I override any appearance methods, so I'm not sure where the error is coming from. Any ideas?

UINavigationController Layout Broken after Presenting Modal

Ever since iOS5, I have a problem where when I present and then dismiss a modal view, my Navigation Controller bar is hidden underneath the status bar. I have read the forums and tried many things but I cannot find the fix for this behavior.
Also, I get this behavior when presenting any modal view controller so it does not appear to be specific to the view controller I am presenting. At first I thought it was a problem with ZXing but this seems to be generic with the iOS5 update.
Additionally, if I select a UITextField after dismissing the modal and my navigation bar is hidden under the status bar, the keyboard comes up misplaced in my window. Again, if I do a rotate back and forth, the navigation controller bar and the keyboard work just fine.
Any ideas would be appreciated.
RESOLVED
OK. I finally found the problem here. Again this only appeared in iOS5 but when my RootViewController launches it holds off on rotations until the animation is done. Once it is done, then it allows rotations again. The problem was that it was returning NO for all aspects (including portrait). The view showed fine but when I would present a modal and return, the view geometry was mangled. Once I changed it to return YES for portrait mode even during animation, the problem went away.
RESOLVED OK. I finally found the problem here. Again this only appeared in iOS5 but when my RootViewController launches it holds off on rotations until the animation is done. Once it is done, then it allows rotations again. The problem was that it was returning NO for all aspects (including portrait). The view showed fine but when I would present a modal and return, the view geometry was mangled. Once I changed it to return YES for portrait mode even during animation, the problem went away.

Substitution of detail view controller does not cause viewWillAppear to be called

I'm porting my iPhone app to iPad. On iPhone I select row in the table, and after that the next view controller is pushed to the top of navigationController (now navigation is performed on the left part of split view controller). For iPad i modified the code this way:
if (deviceIsIPad())
{
UISplitViewController *svc = (UISplitViewController *)[self findNearestParentOfClass:[UISplitViewController class]];
svc.viewControllers = [NSArray arrayWithObjects:[svc.viewControllers objectAtIndex:0],
nextViewController,
nil];
}
else
[self.navigationController pushViewController:nextViewController animated:YES];
There are no problem at iPhone code (when controller is pushed to navigation controller), but on iPad viewWillAppear: is not called (viewDidLoad is called however), while I have a reason to perform some customization right in viewWillAppear:. Why is it not called, and what should I do to force it to be called?
Much thanks in advance!
Not exactly sure what you're trying to do here but simply initializing a splitview controller and adding controllers to it does not force views to appear. You have to add the splitview controller's views to the window.
Here is the code from the Xcode Splitview template's app delegate's applicationDidFinishLaunching:
splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = [NSArray arrayWithObjects:navigationController, detailViewController, nil];
splitViewController.delegate = detailViewController;
NSLog(#"master=%#",splitViewController.viewControllers);
// Add the split view controller's view to the window and display.
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
The views in the navigation controller appear because it is already attached to the window and displaying the view of one of its controlled controllers.
Edit:
From Comments:
All initialization code you are quoted
here is already performed. Now, let's
assume one have a table on the left
controller, then he select another row
there, and want to replace right
controller with another one.
splitViewController.view is added on
the window фдкуфвн, because some GUI
elements initialized in viewDidLoad,
are properly presented on view
It sounds like your problem arises because the detail (right-side) view of a splitview controller is always visible i.e. it only appears i.e calls viewWillAppear, once immediately after first being loaded. There is no point at which the detail side has no view present. I'm not sure what entirely swapping out viewControllers of the splitview will do.
If you want to change the detail view on the fly. You need to put a navigation controller in the right side and then push and pop view-controllers in that nav in response to events in the right side controller.
Look at how the iPad iPod app works. You have a leftside view of playlist and on the right side, a list of all the songs in the playlist. Selecting a song pushes a song detail view on top the list of songs.

UIBarButtonItems not showing up on UIToolbar

I have a UINavigationController with toolbarHidden set to NO.
I have added UIBarButtonItems to navigationController.toolbar.
The toolbar is displayed, but the buttons are not...
What gives?
Ok so apparently I misunderstood the usage of the setItems method on navigationController.
To remedy this I set the toolbar items on each view controller that is pushed onto the navigation controller's stack.
I guess if I wanted to have one toolbar persist through all the views I could add a new toolbar to the view controller holding my navigation controller then simply invoke setItems on my navigation controller.
I digress.

Resources