I was going through Apple's documentation about navigation controller and find this point ambiguous and hard to comprehend.
It was written in this online documentation of navigation controller.
Navigation Controller Views
A navigation controller is a container view controller—that is, it
embeds the content of other view controllers inside of itself. You
access a navigation controller’s view from its view property. This
view incorporates the navigation bar, an optional toolbar, and the
content view corresponding to the topmost view controller. Figure 2
shows how these views are assembled to present the overall navigation
interface. (In this figure, the navigation interface is further
embedded inside a tab bar interface.) Although the content of the
navigation bar and toolbar views changes, the views themselves do not.
The only view that actually changes is the custom content view
provided by the topmost view controller on the navigation stack.
From that, my understanding is that inside this "view" property. There should be at least two subview inside this view.One is the navigationBar the other is the contentView of the current displayed viewController’s view. But while I am debugging only the navigation bar showed with another view called UINavigationTransitionView showed.
My question is, is this normal. Have I done anything wrong?
Second, what is the most common way to access current displayed viewController's view with only the reference to the navigation controller.
Thanks
UINavigationTransitionView controller contains one wrapper view which intern will have the current uiviewcontroller's view.
You can probably find this view as a subview of UINavigationTransitionView. However this is not the "right" way to do this. The proper way is to go through property "topViewController" and then take its view:
self.navigationController.topViewController.view
If there is another view controller or its view that you need, you have access to whole view controller's hierarchy across navigation controller through viewControllers property.
self.navigationController.viewControllers
More here:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UINavigationController_Class/index.html
Related
I've created a view controller that's within the view controller hierarchy of a UINavigationController. This view controller contains a full-frame container view that displays the view of a child view controller.
I'd ideally like to display the items associated with the child view controller's navigation item (i.s., title and rightBarButtonItems) instead of the navigation item of the view controller that contains the container view.
My initial reaction is simply to set viewController.navigationItem = childViewController.navigationItem but navigationItem is read-only. My second reaction is to set the associated items themselves, e.g.:
viewController.navigationItem.title = childViewController.navigationItem.title;
viewController.navigationItem.rightBarButtonItems = childViewController.navigationItem.rightBarButtonItems;
With swizzling, I'm also able to detect when the child view controller's navigation item's properties are set and update accordingly. However, if the child view controller's navigation item's properties are updated without replacement (e.g., title changes text color, rightBarButtonItem is disabled), these changes are not reflected.
Is there any way of achieving the effect I'm looking for?
Hello sir instead of setting childviewcontroller navigation item you can set parentviewcontroller's navigation item.
I mean to say before loading child view controller you can set parentviewcontroller's navigation bar Item if you know childviewcontroller's navigatio bar item value.
I have one more question if you child view controller is inside container how does it have own navigation bar because it would be scrollable so better to set parent view controller's navigation bar before loading child view controller.
Turns out that my problem was elsewhere and that the approach outlined in my question works as desired. x_x
To make this answer more useful, here are more details of my approach:
I created a category on UINavigationItem and swizzled the methods -[setTitle:] and -[setRightBarButtonItems:] using Mattt Thompson's tutorial (http://nshipster.com/method-swizzling/). The swizzled methods simply post custom NSNotifications UINavigationItemTitleDidChange and UINavigationItemRightBarButtonItemsDidChange respectively.
In -[prepareForSegue:sender:] I extract the destination view controller of my embed segue and store it in a custom property childViewController on my view controller.
In the setter for my view controller's childViewController I add observers to my custom UINavigationItem notifications using the navigation item of my child view controller as the object.
When each of these notifications are received, I update the title and rightBarButtonItems of my view controller's navigation item, respectively.
The "elsewhere" in my own fix was that my child view controller wasn't updating its right bar button items when I thought it was, so it appeared as if the view controller presenting it wasn't obtaining its right bar button items.
I've seen a lot of apps that have a universal search bar that always remains at the top of the app. I have implemented a UISearchBar with a UITableView in one view controller. I want to have the same search bar on other view controllers in my app. How do I link these other UISearchBars to the one I have already created? I.e., how do I configure these other UISearchBars so that they return the same search results and link to the same UITableView?
Nested view controllers may be what you need.
Define a “top view controller” that manages a “top view” containing a search bar and add your (table) views to the top view (using -addSubview: on the top view) and the associated view controller(s) to the top view controller (using -addChildViewController: in the top VC and on itself).
In Interface Builder, you can define a top view and inside it a “container view.” The system then handles the insertion of the subview and sub-view controller.
By defining a good view controller hierarchy, you make your app design more logical and clean. I’d recommend to take some time into investigating a good hierarchy before diving into coding.
A final note: the UISearchDisplayController is an object (apparently not a view controller) that superimposes a search bar above a view controller’s view. You might be able to simply apply it immediately above the top-most view controller (the one that is always visible, like a navigation controller). It’s worth looking into it, if you didn’t already. ;-)
An example
View controller hierarchy
XYZTopViewController (managing a XYZTopView)
UINavigationController (managing a private navigation view hierarchy defined by Apple)
XYZFirstPageViewController (managing a XYZFirstPageView) (the “root” view controller)
XYZSecondPageViewController (managing a XYZSecondPageView) (pushed by nav. controller when you need it to)
View hierarchy
XYZTopView
UISearchBar
(private navigation view hierarchy defined by Apple)
XYZFirstPageView
(your view hierarchy belonging to the first page/screen)
Since you only every have one window per app, and view's don't have
levels, you have to make sure that view stays on top of the hierarchy,
no matter what. One relatively easy way is to add it directly to the
window above the rest of the interface (the navigation controller):
How to show a uiview alway on top?
In applicationDidLaunch:
// After the main navigation controller or tab controller has been added
// either programmatically or in the xib:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,70,320,44)];
[self.window addSubview:searchBar];
I've seen a lot of other questions on here about adding a UINavigationBar to a UIPopoverController. All of the examples I've seen follow one of two patterns:
In the init or viewDidLoad method of the Popover subclass, you alloc-init a UINavigationBar directly, as suggested here. This method is a little hacky, and while it shows up nicely, if the popover is a UITableViewController, you have to mess with a bunch of things to make sure the navigation bar you just added doesn't overlap one of your cells.
Alternatively, a lot of post suggest creating a UINavigationController just before presenting the popover, as shown here.
With the second method, however, won't the popover be the only controller in the newly created navigation controller? And if my view that I'm presenting the popover from is itself already in a navigation controller, the popover will NOT be in that same navigation controller, correct? It seems to be that the more appropriate thing to do would be to add the popover being created as another controller in the navigation controller that already exists (and which the controller that presents the popover is already a part of). Is that possible? Or is there a reason why the navigation controller for the popover needs to be independent from the navigation controller for the presenting controller? Or am I totally missing something here?
You have many questions, young Skywalker. :)
Creating a UINavigationController and then embedding the controller you would like to present is the way to go.
Don't get confused by all the controllers involved here:
UIPopoverController is a construct that shows an existing UIViewController in an overlay like style. UIPopoverController itself even isn't a subclass of UIViewController. The name is misleading.
So UIPopoverController hosts another controller. In your case, we let it host a UINavigationController.
UINavigationController is a subclass of UIViewController. It is a container controller and can handle a stack of UIViewControllers.
On that stack we push one UIViewController: the one you want to display and garnish with a UINavigationBar. Since Mr. UINavigationController comes with a build in UINavigationBar, he's our friend.
There is no need to subclass UIPopoverController. You just keep one static reference to it around so you can dismiss the current open popover in case you want to present another.
It does not matter where you present the UIPopoverController from. It will always be a popover. Even if presented from an existing UINavigationController. Only if you use presentViewController: you will get different results depending on the controller you're presenting from (modal or pushed on top of the stack).
won't the popover be the only controller in the newly created navigation controller?
No, the popover will contain the navigation controller and the navigation controller will only contain its root view controller (which would otherwise have been added directly to the popover as its root).
You seem to be a little confused about the relationship between the popover and the popover root view controller...
the popover will NOT be in that same navigation controller, correct
Yes, correct. The popover is effectively a window floating above all other views
Or am I totally missing something here?
Maybe... The popover would usually be used for displaying something modal, transient and smaller than full screen size. Putting a navigation controller in the popover and adding views to it is the normal approach.
Adding a navigation bar to a popover isn't hacky. A navigation bar is just another regular view. That also means that using a UITableViewController with it, the navigation bar will overlap the table view, as the UITableViewController's view property just returns the controller's tableView property. If you want to add a navigation bar above a table view, without it overlapping the table view, use a regular UIViewController and add your navigation bar and table view the normal way. UITableViewController should only be used if your only view within that view controller is a table view.
Having said that, I do agree with others that just using a navigation controller without using its navigation features is the most common approach.
In my app I want to add a view much like the status bar, always onscreen at the top of each of my view controllers, displaying application wide data.
I really have no clue how I might achieve this so any suggestions would be really helpful. I;m sure someone must have chased this idea at some point?
Thanks.
What you're willing to do is fairly straightforward since iOS 5 using view controller containment, it allows you to embed child view controllers in a parent view controller.
In your case I would create a custom container view controller with two subviews: the content view and the statusbar-like view. The content view should display your current root view controller by adding it as a child view controller to your container view controller and adding its view as a subview to the content view. The statusbar-like view can then be used to display information that will be visible everywhere in your app.
You might want to read this documentation for further details:
Creating Custom Container View Controllers
You can create your custom view and add it as a subview to your keyWindow. This way it will always be visible. Another option is if you have tab bar controller or a navigation controller as your root view controller, then you can add that view as a subview to their respective view and it will always be visible.
In my app, i have a main view controller which sometimes brings a modal view on top of it. This modal view is a UINavigationController with a navigation bar. I want to display an image above the navigation bar, and have the navigation bar appear below the image.
I do not want to subclass anything and the app uses autolayout, i do not want a bunch of delegate callbacks and frame calculations. The view inside the navigation controller (the actual modal content) must still respond to different screen sizes correctly, such as rotation, call status bar etc. Also, no IB solutions please, these views are all managed in code.
How do i accomplish this?
I would turn off AutoLayout and place the image at the top
I don't think you can do it with your modal view being a navigation controller. I would do it by making that modal controller a UIViewController that you set up as a custom container controller. You can add an image view to the top of this controller's view and add the view of a child view controller (which would be a navigation controller) to the bottom. This would be a lot easier to do in a storyboard using container views, but it certainly can be done in code.