Why shouldn't I modify the UINavigationController's toolbar? - ios

The documentation for the toolbar property in UINavigationController says:
This property contains a reference to the built-in toolbar managed by the navigation controller. Access to this toolbar is provided solely for clients that want to present an action sheet from the toolbar. You should not modify the UIToolbar object directly.
I can understand why I shouldn't modify the toolbar's visibility or items, because UINavigationController provides an interface to do that. But I've added a button that, when tapped, causes the toolbar to animate offscreen. Why shouldn't I do this?
Must I instead create my own ToolbarNavigationController class that replicates everything UINavigationController does with the toolbar just sod, I can do what I want with the toolbar? Seems like a waste of effort when the UINavigationController already does what I want. Why on earth would the docs suggest I so severely limit what I do with it?

Three ways that having moved the navigation bar might confuse it:
If your app can be rotated, does it stay in the right place after rotation? When it returns to the screen, does it animate on from the right place?
If you display a search bar, the navigation bar animates itself off. If it's already been moved manually, does it know where it is?
If you push a viewController with hidesBottomBarWhenPushed set to YES, and return, does the navigationController put its toolbar back where it belongs?
On the other hand, how about calling UINavigationController setNavigationBarHidden:NO animated:YES - does that do what you want, while letting the navigation controller maintain control of its toolbar?
Edit: Sorry about misreading. Yes, you probably can do what you're asking, as long as you don't also do anything (like item 3 above) that mean the navigationController moves its toolbar around.
On the other hand, the behavior you want can also be achieved as follows:
For the viewController with the multiple toolbars, set its hidesBottomBarWhenPushed to YES.
Place all the toolbars that viewController needs on it, and have it take full control of their positions and visibility.
If the default toolbar has the same layout as the navigationController's own toolbar, this will create the odd visual effect of seeing the same set of toolbar items slide off and then on again, but everything else should work.

UIToolbar is inaccessible because it doesn't need to be accessible. It responds to +appearance just fine. In your case, you can access the properties of a readonly variable (look at CGRect!). If you need to animate a UIToolbar or UINavigatiomBar offscreen, set it's frame.center property equal to a CGPointMake in a UIView animation block.

Related

Are UINavigationControllers equivalent to having manually wiring each VC with a navbar?

If I have the storyboard in the form of (where the arrows are segues)
UINavigationController -> ViewControllerA -> ViewControllerB
Would that be basically more or less equivalent to
ViewControllerA -> ViewControllerB
(NavigationBar) (NavigationBar)
if I manually wire each NavigationBar up to Button Bar Items with event listeners attached to unwind segues?
Or does UINavigationController offer something more than that?
UINavigationController is what is known as a container view-controller: it takes a bunch of other view-controllers and manages how their views are presented on the screen. UISplitViewController is another example of a container view-controller.
In the case of UINC, it:
Allows pushing a new "top" controller, animating it with a left-to-right/right-to-left animation depending on locale
Remembers the stack of previous top controllers, allowing you to pop back to them
Adds a UINavigationBar view above the top controller's view so the user can pop back by themselves (you can disable this)
Sets a layoutMargin on the top controller's view, so it can adjust content to not underlap the bar
Provides an edge-swipe gesture so the user can interactively pop to previous controllers (slowly peel the top sheet back)
For more information, including about how to create your own container view-controllers, see Apple's documentation on the subject: https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html
There are some differences.
One that I have noticed, a UINavigationController will handle correctly putting the navigation bar in the right place for iPhone X vs other models (It will make the height larger so it goes into the wedge of the of screen, while just a nav bar will remain its standard height).
If you just put a nav bar on each UIViewController, you are going to have to check on each view controller if you need to update the bar size.
Basically the navigationController provides you many thing that you might use: A stack of UIViewControllers, a navigation bar, a toolbar, delegate methods, show/pop animations, etc. Doing all that by hand will be not too appropriate and a little bit messy. I suggest you to take a look to the Appleā€™s documentation for UINavigationController. There you will get a much better understanding of all the capabilities and methods that this class provides you.

Dynamically increase/decrease height of custom UINavigationBar

I followed this blog post that explains how you can implement a custom UINavigationBar that has an increased height, if for example you wanted to put additional ui elements in the nav bar underneath the rest of the bar content that will persist between navigation on the stack. This code works really well in the case where you always want it to be that increased height.
In my app, I need to start the navigation bar at its default height, then increase it later, adding more content, after the user performs a given action. Very similar to the song info and controls in the iTunes Store:
So I put some checks in place to not reposition anything if a BOOL property is NO. When I set it to YES, I call [self setNeedsDisplay] which will call layoutSubviews to position everything appropriately based on that boolean value. sizeThatFits is also called and I return the proper height.
The problem is, I can't call [self setTransform:CGAffineTransformMakeTranslation(0, -(NavigationBarHeightIncrease))]; in initialize. Instead I call that at the same time I change the boolean value to YES. Because of this, all of my elements are moved up that amount. But if I don't call setTransform, the elements in the nav bar are in the proper position, but the bar itself is positioned too far down, so that the custom view I've added to the bar is shown overtop the view controller's view - it bleeds out, and the extra space I added is black not the navigation bar's background color.
If I call setTransform in initialize, when the height is the default height, the elements are moved up when they shouldn't be.
So, how can I properly dynamically change the height and positioning of a UINavigationBar subclass?
As suggested in the comments, to achieve the behavior where a custom navigation bar (not subclassing the native control) persists across pushes and pops of controllers in a navigation controller, you'll need to have a single controller with the custom navigation bar and then a single embedded view that resolves to a UINavigationController with its view controllers underneath. Then, it will also be necessary to set the navigation controller's delegate to the root controller so that the title and other properties can be updated as the sub-controllers are pushed and popped. I've provided a screenshot below of what the storyboard version of this might look like:
An option is to create a UIViewController in storyboard which has only the control view you wish to show below the navigation bar with everything else transparent. The advantage here is you design this using the normal tools. Use constraints to place it just below the navigation bar and to set the heights, widths etc of your views.
When you wish to show the control, you can create an instance of this UIViewController and remove the content view from it and add it to the view hierarchy of what is on screen.
There are two options for inserting the extracted base view:
If you add this control view to the view controller at the top of
the navigation stack (what is on screen), it would be covered when
you push on a new controller. This is not what you said you wanted.
If you add this control view to self.navigationController.view, then
it will persist across pushes and pops. This is what you said you wanted.
I use this approach to provide popup help bubbles to describe what is on screen. Depending on whether I use option 1 or 2, I can persist help across multiple pushes/pops.
I got the idea from this tutorial which describes the general approach: http://blog.typpz.com/2013/12/09/ios-sdk-create-a-pop-up-window/
That link provides a full code example on how to bring up the view and remove the view.
This would let you design it in IB, present and dismiss it as required and persist it across navigation sequences.
Hope this helps.

UIDynamicAnimator - Can I use the main UIWindow as the reference view?

I'm working on an app that uses a UITabBarController, where each tab contains a UINavigationController stack.
I'm attempting to create a UIDynamicAnimator that will use UIDynamicBehaviors to animate in, from the top, a UIView from under the UINavigationBar, such that it collides with the UITabBarController's UITabBar and pushes it off the screen.
In order to achieve this, my reference view for the UIDynamicAnimator must contain the UITabBarController's view.
Is it okay to use the UIWindow instance as the reference view for the UIDynamicAnimator?
(Please provide feedback on this approach as well, I see others modifying the frame of UITabBarController.tabBar - is that bad practice?)
Any view will do. All you are doing is setting the frame of reference.
Be aware, however, that the window does not rotate when the device rotates (window coordinates are screen coordinates, and are fixed with respect to the device). Thus, working with its coordinates can be a nightmare.
Given the nature of your question, since everything is happening inside the tab bar controller, I don't see why you don't use the tab bar controller's view. It contains the tab bar and the various views of the child view controllers. If it is the root view controller (as I suspect it is), it contains absolutely everything else, and it itself is not going anywhere.

Need a solution for a UINavigationController, with a back button to the application

In my app I would like to have a way to make the user go to a website, but not leave my app.
I do that with a UIWebView.
I'd like to constantly have a "Back to app" button on the NavigationBar on the top.
The rest of the Web navigation (back, forward) should be on the top as well, but appear and disappear with the context.
The problem is that I don't know how to make the buttons appear on the UINavigationController.
How can this be done?
I have my UIWebViewDelegate set up to receive all the relevant functions, but the buttons don't appear.
EDIT: I need to solve this programmatically
As of iOS 5, there are leftBarButtonItems and rightBarButtonItems (note the plural) properties on UINavigationItem, so there's a way to have more buttons. It seems to me that these are only accessible programmatically, but not from Interface Builder.
If you can, my suggestion is to not just add the Web View as a subview, but to give it its own ViewController and push that on the Navigation Controller's stack. That'll give you your back button for free (that's kind of what UINavigationController was designed for, after all). And it should help you to keep the web browsing code separated form the other stuff in your app.
NB: in your case, you'll have set the leftItemsSupplementBackButton property of the Browser View Controller's navigationItem to YES to get the automatic back button (the details are in the documentation)
You can not have more than two buttons on the NavigationController's navbar without doing some tricks. There are two properties self.navigationItem.leftBarButtonItem and self.navigationItem.rightBarButtonItem. If not, add a custom view and place the buttons there. A good way to implement multiple buttons with multiple actions is shown here.

Adding a toolbar to a navigation controller

I am completely new to ios development and I am only interested in developing for ios5.
I have an app with some scenes, they are mostly tableviews. I also use a navigation controller
I however need to add some status text and buttons that are always visible in all scenes and thought that a toolbar added to the navigation controller should do the trick.
so i thought that i should only have to drag out a toolbar in storyboard to the navigation controller, but it does not stick there. I can add it to the bar underneath with first responder and navigation controller but that does not help me (small icons).
I can also not add it to my table view (but if i drag out a plain view I can add it there)
do I have to make my own custom navigation class that the navigate view uses and then programatically add my toolbar?
Had the same question recently. Check Attributes Inspector in your ViewController's properties in storyboard. There you can define a Bottom Bar.
Well, inside the UINavigationController, you should have something... A UIViewController for instance. You can easily add a UIToolBar by dragging the object inside the UIView of the UIViewController. What might being happening is that as the root view you have the UITableView, in that case I think you can't do that. But to better understand, just take a small print screen of your StoryBoard.
If you zoom up to 100% on the storyboard it should drag.

Resources