Go back two levels with UINavigationController and Storyboard - iphone

I have a Storyboard with three views A, B and C (and more). From view B, the user can go back to view A by tapping the left button bar item that's automatically created. I've changed the label on this to "Cancel" by setting the Back Button property of A's navigation item.
C should have the same Cancel button, but it doesn't make sense to go back to view B; rather it should jump back to A. I know how to do this programatically but where do I put the code so it's triggered when C's Cancel button is tapped?

I found this way simplest, just put index:
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];

I think you cannot override the back button function. What I do in those cases, is create a left bar item and on it's function decide which navigationController view to send the user to.
I do that by using:
[self.navigationController viewControllers] objectAtIndex:1];
Index 1 will be the first view after the root viewController, if A is your rootView, you can also use:
[self.navigationController popToRootViewControllerAnimated:YES];

If you want to cancel an action from B or C and go back to A it may make more sense to present the view controllers modally.
From the iOS human interface guidelines:
A modal view generally displays a button that completes the task and dismisses the view, and a Cancel button users can tap to abandon the task.
Use a modal view when you need to offer the ability to accomplish a self-contained task related to your application’s primary function. A modal view is especially appropriate for a multistep subtask that requires UI elements that don’t belong in the main application user interface all the time.

Instead of pushing C, use the replace option in the segue so that it will do this automatically for you.

Another option: if you put the following into B, it will remove it from the stack when C is presented. Then the stack looks like [A,C] so back will go straight to A.
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// remove this view controller from the stack
if let nav = self.navigationController {
let vcs = nav.viewControllers.filter {(vc) in
return (vc as? MyViewController) == nil
}
self.navigationController?.setViewControllers(vcs, animated: false)
}
}

Related

Dismissing 3 layers of modal view

In my application, I have three layers of modal view controllers.
1) So my rootViewController is a tabbar.
2) On applicationDidFinishLaunching I am presenting a viewController, say viewController A modally above tabbar.
3) On click of a button in 'viewController A', I present another viewController B modally.
4) And a button action on viewController B presents navigationController modally with its rootViewController as viewController C.
5) Finally from viewController C, I want to go back to viewController A.
I tried using
[[[self parentViewController] parentViewController]dismissModalViewControllerAnimated:YES];
in viewController C, but it reverts me back to viewController B instead of viewController A.
How can I revert back to viewController A.
Any help would be appreciated.
push all the 'modal' views on a navigationControl with a transitionStyle that looks like the modalTransitionStyle..dont reinvent stuff
present all in one modal navigationController Id say :)
You've got a complex VC stack. You could as one poster suggested implement a custom dismissView method, but that would be fragile: if you reuse this view, or move it in your app, it will cease functioning, because it relies too much on specific knowledge of how other VCs have configured their state.
You could configure a delegation chain. This would be the standard way to manipulate views: the presenting VC is also responsible for removing anything it presents.
To do so, build a protocol implemented by B, and initialize C with a reference to B. Similarly, initialize B with a reference to A (with potentially the same protocol, depending on any other communication that needs to be passed between them.)
Then when the button is clicked on C, it calls B's delegate method. B unwinds C as appropriate, and calls A's delegate method. A unwinds B as appropriate.
This has the advantage of keeping VC knowledge encapsulated: A knows how it presented B, so it knows how to unpresent it, and B knows how it presented C, and knows how to un-present it. In no case does one VC need to make assumptions about how it was presented by another.
Implement the view dismissing method like:
- (IBAction)dismissView
{
[self dismissModalViewControllerAnimated:NO]; // dismiss c
[[self parentViewController] dismissModalViewControllerAnimated:NO]; //dismiss b
}
[self parentViewController] will return the parent view of viewControllerC, that's viewControllerB. So it'll dismiss that view too.

How to hide the Back Button of UINavigationController?

I've tried various ways of hiding the back button of my UINavigationController
UINavigationController RVC = new UINavigationController();
other code
RVC.NavigationItem.SetHidesBackButton(true,true);
and other similar ways, but none of them have actually hidden the back button. Does anyone know what I'm doing wrong?
Use this.NavigationItem.SetHidesBackButton(true,true); within the controller that it is pushed in the navigation controller. You could override ViewWillAppear and put the code there like:
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
this.NavigationItem.SetHidesBackButton(true,true);
}
Hope it helps.
A simple note
Since the navigation bar is unique for a UINavigationController, the button will maintains its state for all the controllers you push in the navigation controller. To explain the concept suppose you have two controllers, say A and B. You first push A and in its ViewWillAppear method you hide the button. When you push B, the button still remains not visible. If you want to unhide the button in B, you can play with its ViewWillAppear method (like before) and so on...

Present ViewController without Back button

I'm having an annoying problem which takes the best off me :<
I've got 3 view controllers, one to show an advertisement in detail which also has a toolbar. Now what I want is, if the user presses the facebook icon on my toolbar in the first view it has to perform a check. If the check turns out false it needs to go to a 2nd view which shows a login screen. If the user logs in here it has to go to a 3rd screen which shows a simple input + button to update their status.
When the user is at this third screen there should be a button "Back", but this button shouldn't bring them back to View2 but it should bring them back to View1 (the advertisement detail screen).
I figured that I wanted to show the 2nd screen (if check turns false) without pushing it but keeping the NavigationBar + TabBar presented. I added some screenshots to clarify.
First view
Second view
I want this view to be presented without using PushViewController but keep the NavigationBar and TabBar.
Third View
I hope this is enough information, hopefully someone can help me.
Thanks in advance!
Perhaps the most natural thing to do here is to present the login view controller modally. When the user has logged in successfully, the first controller can then push the third view controller onto the navigation stack. This way, the back button will lead directly back to the first view controller, and the user will understand why.
So if we have three UIVIewControllers:
DetailViewController
FacebookLoginViewController
UpdateViewController
We have two viable options:
1) Upon successful login...pop the current LoginViewController and then push the UpdateViewController
PopViewController(detailViewController, false);
PushViewController(updateViewController, true);
2) Present the login Modally and simply present the UpdateViewController
PushModalViewController(loginViewController, true);
//ascertain result of login
if(IsLoggedIn) {
PushViewController(updateViewController, true);
}
consider the following view push logic.
if (!login) {
LoginViewController *lvc = [[[LoginViewController alloc] init] autorelease];
[self.navigationController pushViewController:lvc];
}
else {
ThirdViewController *tvc = [[[ThirdViewController alloc] init] autorelease];
[self.navigationController pushViewController:tvc];
}
It is : create and push the view controller when it is needed.
Try to pop the current view controller (without animation i guess) before pushing the new one. If there is only one view controller in the navigation stack no back button will be shown.
Haven't used Monotouch but i guess something like this:
this.NavigationController.PopViewControllerAnimated(false);
this.NavigationController.PushViewController(newViewController, true);
But as Caleb suggests it's probably better figure out a way that fits the normal navigation behaviours instead of hacking around it.

pushing onto navigation stack not within the nav controller

I have this ad class which contains an UIImageView. I've added an instance of the class to my appdelegate onto the "window" view.
Now, I want to, when the user taps the ad, push my "detailedViewController" onto the current navigation controller, which all of my tab bar items contain. I don't know if it is possible.
Perhaps, I should just add my advertisement class to every view controller for every nav controller. However, if the user pushes or changes a view controller it would reset the class.
I just want to overlay the ad once.
EDIT:
Let me rephrase, can I from the app delegate and from my object know which tab bar item is selected? If I can determine which tab bar item is selected I can point to the appropriate nav controller instance.
The easyiest way would be to present your DetailVC as a ModalView which also makes sense in semantics.
Yes, it is possible to detect which tab is selected but it is easier to use the selectedViewController-property of UITabBarController.
UIViewController *curVC = myTabBarController.selectedViewController;
if([curVC isKindOfClass:UINavigationController.class])
{
UINavigationController *nav = (UINavigationController*)curVC;
[nav push...];
}
else
{
// do sth else: go to webpage for instance
}
Whoever owns the tab bar controller can do
[myTabBarController selectedIndex];
or
[myTabBarController selectedViewController];
The first one returns the index of the selected item, the second one the actual view controller, you might be better off with the first one.

How To Change UIButton Of Same UIViewController?

I have a a UIViewController that is pushed to by two different views in my app.
One time it is a modal view, so I have the right navbar button set to Done and it dismisses the view.
At another time in my app, this same view is pushed to, but not modally, thus I don't want this button to show. I tried adding this when pushing it, but no luck.
self.navigationItem.rightBarButtonItem.enabled = NO;
You can check the parent view controller for whether it has the modalViewController property set
if (self.parentViewController.modalViewController == self)
{
// add button
}
Simple and effective -
self.navigationItem.rightBarButtonItem = nil;
Edit:
How can you add this when you are pushing this ? Add it in the viewWillAppear or viewDidLoad of the viewController you want to see this is in.
You can check for a certain condition.
If it is pushed from view 1, you can make it nil.
If it is shown modally from view 2, you can make it appear.
For this, you will have to make the viewControllers communicate with each other. For that, you will need to use NSUserDefaults and set an integer for a key.
You can assign two different integers logically and use them as the condition for showing/not showing the rightBarButtonItem.
Good Luck.