How do I get a reference to the UINavigationController's backBarButtonItem from the UINavigationController at the top of the stack. In some circumstances I want to disable going back until some networking code is complete.
self.parentViewController.navigationItem.backBarButtonItem.target =
self;
self.parentViewController.navigationItem.backBarButtonItem.action =
#sel...;
doesn't work
delegate method
- (BOOL)navigationBar:(UINavigationBar *)navigationBar
shouldPopItem:(UINavigationItem *)item
doesn't work either.
An answer and a recommendation:
The answer: I would recommend you change your MVC model slightly to have a BOOL property in your model that is on or off depending on whether the network activity is done and then use a delegate/protocol adopted by your QuestionsVC that updates the back button setting as that property changes. You would need to add the following in the delegate method in QuestionsVC:
[self.tabBarController.navigationItem setHidesBackButton:YES animated:YES];
I tested it and it works.
The recommendation: It is never recommended to have UITabBarController inside a UINavigationController (only the inverse is recommended). I would adjust accordingly before you get too deep into your project.
Update:
I can understand the need for a mainVC as startup VC with a button to "start" if you will. You are correct that you need a NavController to be able to push/pop VCs and use segues in Storyboard. But that is not the only way to display a sequence of VCs, you can present/dismiss VCs. So in your case:
1- I would delete the first NavController
2- Make the MainVC the starting VC (entry point) by moving the arrow on the left of the NavController to the left of MainVC
3- Disconnect Main VC from TabBar controller (delete that link) because you will not be able to use segues in SB without Nav Controller. You will have to instantiate and present that tab bar Controller.
4- Add a new object file (.m/.h) - a subclass of UITabBarController and change the class of the tabBarController in IB to the name of your subclass. You might have to build/clean or restart xcode if it does not show on the dropdown of the class list in IB.
5- Create an IBAction method in your mainVC and link it to the button in Main VC.
6- In that method (in your Main VC), add the following code:
yourTabBarControllerSubClassName* myTabController= [self.storyboard instantiateViewControllerWithIdentifier:#"theTab"];
[self presentViewController:myTabController animated:YES completion:nil];
7- Make sure that in your SB that you select the tab bar controller and in the identity inspector, put the SB ID as "theTab" and check "use SB ID".
8- if questions VC or status table VC have a sequence of VCs within each, you can embed each VC in a Nav Controller and that would be ok.
With that the case, you might not need to worry about that back button since it won't exist anymore!
Good luck
Hope this helps.
Related
My app's storyboard is using UIViewController's to go to different views of the app. However, I want to try a third party library, that is EGOPhotoViewer, not to reinvent the wheel. But how do I add UINavigationController to UIViewController from the storyboard? Here is the code this library is using to initialize.
EGOPhotoViewController *photoController = [[EGOPhotoViewController alloc] initWithPhotoSource:source];
[self.navigationController pushViewController:photoController animated:YES]
It only works for me when I add it as a view controller:
[self presentModalViewController:photoController animated:YES];
but the library works best within navigation controller because title bars and navigation buttons are missing from my testing approach.
In the storyboard
select your original viewController, then in the menu:
Editor -> embed in -> Navigation Controller (that viewController becomes the rootViewController)
Now you have various options to push your photoController eg:
From a UI widget in your rootViewController, CTRL-drag to photoController. That will create a segue which should work without extra code (although it helps to name the segue so that you can refer to it later in code)
or in code as you have in the question.
So I've got a UITabBarController as a view in my app, the first tab of which is a UINavigationController. I have a button inside this view that pushes another, custom view onto the stack:
myViewController *vc = [[myViewController alloc] init];
[self pushViewController:myOtherView animated:YES];
The class myViewController has things that are supposed to happen inside of both -viewDidLoad and -viewDidAppear:(BOOL)animated, but if I hit my button right after the UITabBarController view appears, neither of those methods seem to be called. And even stranger, when I hit "Back", the view does not animate away, but rather the view underneath it in the stack just pops back into place.
However, if I go to another tab in the tab bar, then go back to the first tab, then hit my button again, my custom view controller animates in, -viewDidAppear:(BOOL)animated is called, and it animates out of view upon hitting "Back" like it should. Unfortunately, -viewDidLoad is never called.
I'm really trying to get away from using Interface Builder for everything; I want to create this view controller purely programatically, but these weird issues aren't helping. Can anyone think of a reason for this bizarre behavior?
Edit: Even if I create my view controller via IB, this behavior still occurs. What's the deal? Do I need to do something to the UITabBarController?
Edit #2: Here's how I want my views to bet set up:
UITabBarControler
Tab 1: UINavigationController
UIViewController to be pushed onto the stack
UIViewController to be pushed onto the stack
etc (possibly more UIViewControllers)
Tab 2: UIViewController
Tab 3: UINavigation Controller
UIViewController to be pushed onto the stack
UIViewController to be pushed onto the stack
You don't say what kind of object contains the code you posted but, if it's handling a button action, it's probably a custom view controller that's managed by your navigation controller. If that's true, then you'd want [self.navigationController pushViewController:myOtherView animated:YES];.
(If self is some other kind of object or self.navigationController is nil, then you would need to add some more details about your current view controller structure.)
I started my application as a navigation based application, and did the main bulk of the work using the tables etc. Now, I wish to create a start view page, consisting of two buttons, one which links to the RootViewController and the other which links to another view.
Is this possible? If so, how would I go about it?
Thanks!
Make a new UINavigationController with your startview as the rootViewController for the window first.
Then on the click event of the first button, make a delegate call to your appDelegate class and remove the present UINavigationController and add the main UINavigationController (which links to your RootViewController) as the rootViewController for the window.
For the second buttons click event you can simply push the next view to the navigationController.
I've been reading the Head First iPhone Development book and I understand how to get to a new view from a table but how exactly would I be able to get to a new view or view controller, by just simply pressing a button? Is that even possible?
I mean there are some apps where you click a button, not a table cell and it loads a new view. How exactly is that done? If someone could help out a newbie it would be greatly appreciated!
I think what you're looking for is a modal vew controller. THis presents a modal view like you described on top of everything else. If rootViewController is the view controller that is displaying your current view, and myNewViewController the view controller you want to display modally:
[rootViewController presentModalViewController:myNewViewController animated:YES];
There's plenty of examples of this kind of thing on the net, just search for presentModalViewController
Like bpapa said in the comments, it's hard to be specific without code. However, generally what you want to do is:
Build a navigation controller that contains one original view.
Create a button in your original view using the Interface Builder.
Build a callback method (usually defined with IBAction) that is run when the button is pushed.
In that callback method, create a new view and push it onto the navigation controller the same way you would using a table view cell.
Alternately, if you only want one level of hierarchy, you could use a modal view controller; instead of pushing onto the navigation controller in the last step, just present the modal view controller.
The general answer is that you have an object that manages which view controller loads when.
The most commonly used is the UINavigationController. It is a UIViewController that instead of controlling views, controls other view controllers. It works like a simple stack. You push views you want to display onto the nav's controller stack and when you want them to disappear you pop them off.
A common (though sloppy) way of using a nav is to make it a property of your app delegate. Then anywhere in your app you can references it by:
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
The view controller for the first the user sees is held in the nav's topViewController property. If you want to load a view based on a user action in the topViewController.view, you would have something like this:
- (IBAction) loadNextView:(id) sender{ // Action called by a a UI event such as a button press.
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
UIViewController *nextViewController=...// load from nib, connect with IBOutlet, create programmatically
[nav pushViewController:nextView animated:YES];
}
The first view disappears to be replaced by the next one. To return to the first view, you have a method in the next view controller like so:
- (IBAction) unloadSelf:(id) sender{ // Action called by a a UI event such as a button press.
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
[nav popViewControllerAnimated:YES];
}
... and the nav returns you automatically to the previous view regardless of what that view was.
When you first start out, especially if you use Interface Builder, the structure of the app is largely hidden. Behind the scenes all view controllers and their views exist in a hierarchy of some kind that leads back up to the app delegate. You should train yourself to think in hierarchal terms even if it is not immediately obvious how that hierarchy is constructed.
Here goes stupid question again :(
I am trying to create a sample app that loads UIViewController when the app loads and the view contains a button to load UINavigationViewController.
Now I created a project with "Window-based Application" and added "RootViewController" in the project with .m, .h, and .xib.
Next I added a view and a button in the "RootViewController.xib" file and it runs ok. After that, I added "UIViewController subclass" file naming "NavViewController" with .h, .m and .xib files.
Also I added - (IBAction)buttonPressed:(id)sender function in the "RootViewController" classes to load NavigationViewController.
Here is the code of the "buttonPressed:".
- (IBAction)buttonPressed:(id)sender {
NavViewController *navViewController = [[NavViewController alloc] initWithNibName:#"NavViewController" bundle:nil];
self.navController = navViewController;
[self.view insertSubview:navViewController.view atIndex:0];
[navViewController release];
}
When I "build and go," it runs fine initially until I press the button. When I press button, program terminates it.
What am I doing wrong? Please help...
Thank you.
What are you doing wrong? Designing your app in a non-standard way - you are not supposed to be able to do this - the NavigationController is in charge!
Why would you have a button that then adds a navigation controller? - it goes against the user interface guidelines. I found it hard to get to grips with the interface guidelines to begin with but you really must because it will make your app so much more usable.
If you need a navigation controller then add it to the view to begin with - or create a new view with the navigation controller. Honestly try it out and you will feel the user interface feels much better.
If you really want a button that adds a navigation controller to the window then do the following:
Keep a reference to the AppDelegate in your code
Use this reference and pass in your current view controller to a method called reloadMainViewWithNavBar:(UNViewController*) viewController
This new method should remove the old mainViewController and create a NavigationController
using your viewController as the root view conroller
add the navigation controller view to window view
The navController has no view set in the nib. A UINavigationcontroller needs a root view. In the nib, connect the navigation controller to a another view controller.
In addition to the fix mentioned above, what you really want to do is create the first view controller contained in the navigation controller - but hide the navigation bar until the button press causes it to unhide. You cannot easily add a navigation controller to a view you are in.