I have looked around but haven't found a satisfying answer. My problem is that whenever I call popToRootViewControllerAnimated:(BOOL) it is not doing anything. When I NSLog it, it logs (null).
Let me back up a bit here. I have a table view controller that has a list of things, at the navigation bar up top there is an option to add and that takes me to a new view controller with a segue "Present as PopOver" which gets rid of the principal or main navigation bar. So I made one manually and added 2 bar button items "Cancel" and "Add". When "Cancel" is tapped, it should take the user back to the table view controller and discard changes, when "Add" button is tapped, it should also take user back to the previous table view controller with the changes. But it's not doing anything.
Here is my code.
- (IBAction)cancelButton:(UIBarButtonItem *)sender {
UINavigationController * navigationController = self.navigationController;
NSLog(#"%#", navigationController);
NSLog(#"cancel tapped though");
ListingTableViewController *rootController = [[ListingTableViewController alloc] init];
[navigationController popToRootViewControllerAnimated:NO];
[navigationController pushViewController:rootController animated:YES];
}
As far as the segue, this view controller is not connected to anything, or should I connect it? This is a noobish question indeed. Here is my xcode screenshot.
Check this link for the screenshot of the storyboard
http://i.stack.imgur.com/lqnCF.png
You must call
- (IBAction)cancelButton:(UIBarButtonItem *)sender {
NSLog(#"cancel tapped though");
[self dismissViewControllerAnimated:YES completion:nil];
}
instead of popToRootViewControllerAnimated because your VC presented and not pushed!
When presenting a view, you are not pushing it in your navigation controller, but having it presented. To dismiss it, try using [self.presentingViewController dismissViewControllerAnimated:NO completion:nil].
Related
I have seen this topic (iOS Storyboard Back Button) and more about this subject, but I still cannot have my back button appear on screen :
I have got 2 viewControllers, the two of them have a navigationController, the "father" controller has the button bar item set to "back" as a plain text, the second viewController appear well with a modal segue, or with "show detail (replace)" segue, but nothing appear on the navigation bar to come back... Would you know why?
Here is a capture :
Thanks
EDIT :
with a custom transition, and when presenting the controller via the navigator in the code, the back button is not here anymore... would someone know why?
When I comment out presentViewController:secondViewController, the back button is here, but the custom animation is not triggered anymore, there is the normal transition set in the storyboard.
Here is my method :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
NSLog(#"segue descr %# : ", [[sender class] description] );
if ( [[segue identifier] isEqualToString:#"second"] ){
//SecondViewController *secondViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"SecondViewController"];
SecondViewController *secondViewController = [segue destinationViewController];
secondViewController.transitioningDelegate = self;
[self.navigationController presentViewController:secondViewController
animated:YES
completion:nil];
Modal and show detail segues don't use a back button, because they are like independent views.
The idea is that those those views are only showing extra information or details about something and you can close them programmatically when you need to go back to the previous view.
A show or push segue will give you the back button in your navigation controller, because that segue is meant to be more like a sequence of views.
When you push a View Controller, you get the back button for "free" without having to write extra code. When you present a modal View Controller, you need to add your own way of dismissing the modal view. Since you have a Navigation Controller, your easiest route is probably to add a UIBarButtonItem to the navigation bar, and have that bar button call dismissViewControllerAnimated:completion: in your UIViewController subclass.
This seems to be the patten used throughout Apples applications; Creation of a new record is done through a modal View which needs to be saved or canceled to continue, and editing a record is done through a view pushed onto the navigation stack.
It doesn't seem right to be basically duplicating my ViewController for 'add' and 'edit' but there are several differences in how pushed and modal ViewControllers work which complicate things.
How should I be doing this so it can cover both bases?
-
Differences include.
When pushed onto the stack the navBar appears at the top of the View and can be configured to contain the cancel/save buttons. When presented modally this is not the case so to duplicate the interface a toolbar needs to be created separately and close/save buttons added to this instead.
When dismissing a pushed view we send a message to the navigation controller [self.navigationController popViewControllerAnimated:YES];, when dismissing a modal view we send a message to self [self dismissModalViewControllerAnimated:YES];
You could add the UIToolbar in InterfaceBuilder, and then just hide it in viewDidLoad when self.navigationController is not nil.
As for dismissing, you could have something like:
- (void)didCancel {
[self.navigationController popViewControllerAnimated:YES] || [self dismissModalViewControllerAnimated:YES];
}
This will shortcircuit if your viewcontroller is part of a navigationcontrol, and use dismissModalViewControllerAnimated otherwise.
This should work for your cancel button. For your save button, it is useful to call some sort of delegate method such as:
- (void)didSave {
// do your saving juju here
if([self.delegate respondsToSelector:#selector(viewController:didSave:]) {
[self.delegate viewController:self didSave:whatJustGotSaved];
}
[self.navigationController popViewControllerAnimated:YES]; // noop if currently modal
}
In the delegate's implementation then, you can put:
- (void)viewController:(UIViewController*)viewController didSave:(NSObject*)whatJustGotSaved {
// do stuff with parameters
[self.modalViewController dismissModalViewControllerAnimated:YES]; // noop if not modal
}
I need a little help on a problem with navigation controllers.
I have a navigationController with 4 ViewControllers pushed. The last vc I push presents a further ViewController modally. The modal ViewController presents an ActionSheet. Depending on the user's answer I either dismiss the modal ViewController only or I want to go back to the root ViewController.
In the ViewController presented modally I have:
- (void) dismissGameReport
{
[[self delegate] GameReportModalWillBeDismissed:modalToPopToRoot];
}
In the last ViewController pushed onto the navigationController stack I have:
- (void)GameReportModalWillBeDismissed: (BOOL)popToRoot;
{
if (popToRoot)
{
[self.navigationController popToRootViewControllerAnimated:NO];
}
else
{
[self dismissModalViewControllerAnimated:YES];
}
}
Dismissing the modal view controller works fine.
However,
[self.navigationController popToRootViewControllerAnimated:NO];
does not cause the root ViewController to display its views. Adding some log info I see that after the message to self.navigationController the stack is correctly popped but execution continues sequentially. The screen still shows the view of the modal ViewController.
As a workaround I tried always dismissing the modal view controller and in the ViewWillAppear method have the popToRootAnimated message. No difference. Still the stack of controllers is popped but the screen continues showing my modal view controller's view and execution continues sequentially.
Could someone help me please?
I like these deceptive questions. It seems very simple, until you try to do it.
What I found was that basically you do need to dismiss that modal view controller, but if you try to pop from the navigation controller on the next line things get mixed up. You must ensure the dismiss is complete before attempting the pop. In iOS 5 you can use dismissViewControllerAnimated:completion: like so.
-(void)GameReportModalWillBeDismissed:(BOOL)popToRoot{
if (popToRoot){
[self dismissViewControllerAnimated:YES completion:^{
[self.navigationController popToRootViewControllerAnimated:YES];
}];
}
else{
[self dismissModalViewControllerAnimated:YES];
}
}
But I see you have 4.0 in your question tags. The solution I found for <iOS 5 is far less pretty but should still work, and it sounds like you were already on the trail. You want viewDidAppear: not viewWillAppear:. My solution here involves an ivar, lets say:
BOOL shouldPopToRootOnAppear;
And then your GameReportModalWillBeDismissed: would look something like this:
-(void)GameReportModalWillBeDismissed:(BOOL)popToRoot{
shouldPopToRootOnAppear = popToRoot;
[self dismissModalViewControllerAnimated:YES];
}
And your viewDidAppear: would look like this...
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (shouldPopToRootOnAppear){
[self.navigationController popToRootViewControllerAnimated:YES];
return;
}
// Normal viewDidAppear: stuff here
}
I have a view that uses a modal view with a page curl to allow for a username to be entered. This username is then verified with a web-based service to see if it is valid.
Everything works great until you enter an invalid username and click outside the modal view. This still checks the username, which is reported invalid and a UIAlertView opens. However, it goes back to the parent view.
Is there any way to get the modal to not dismiss in this case?
I have tried to reload the view but either it isn't working or the UIAlertView is blocking it. The last idea I have is to couple displaying the modal view with the "OK" on the alert for an invalid username. Anyone have any ideas?
If you were not using a UINavigationController You could put something like this in the view controller that calls the modal view:
-(void)dismissModalViewControllerAnimated:(BOOL)animated{
if (_someFlagForBeingProperlyLoggedIn) [super dismissModalViewControllerAnimated:animated];
}
When you tap on the page curl the presenting/parent view controller is sent dismissModalViewControllerAnimated:.
Since you are using a navigation controller your options are limited. This is because UINavigationController is a subclass of UIViewController, and a self centered one at that. When you click the page curl it's dismissModalViewControllerAnimated: is being called.
You still have the option of subclassing UINavigationController and implementing the above method, but that will get messy in a hurry.
Having the UIAlertView "direct back" to the modal login view IS very easy. Have that main view conform to the UIAlertViewDelegate protocol. When you display the alert set that instance as the delegate, and in that class implement the method:
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
// Enclose in if (buttonIndex == #) for selective calling
UINavigationController* nav = (UINavigationController*)[[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"Preferences"];
[nav setModalTransitionStyle:UIModalTransitionStylePartialCurl];
[self.navigationController presentModalViewController:nav animated:YES];
}
Then when the alert view is dismissed it will show the 'login' view.
You should redisplay your modal view with a little delay, something about 0.3-0.5. that's the amount of time needed to alert to be dismissed and that is exactly animation(the dismissing of the alert view) that prevent the modal view from showing up.
-(void)showModal{
SomeModalViewClass* modalView = [[SomaModalViewClass alloc]init];
[self setModalTransitionStyle:UIModalTransitionStylePartialCurl];
[self presentModalViewController:modalView animated:YES];
[modalView release];
}
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
//check the button index if needed and then
[self performSelector:#selector(showModal) withObject:nil afterDelay:0.3];
}
I have a problem about viewController. I created a program What is viewController based applicaiton. There is 4 button on mainViewController. I used this code for calling mainviewController
-(void) applicationDidFinishLaunching:(UIApplication *)application{
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
}
Then click to any button on homePage , I go to the other viewController. This code for call another viewController belong
-(IBAction)clickCalendarButton{
calendarButton.selected=YES;
[calendarButton
setImage:[UIImage imageNamed:#"afvalkalender_pressed.png"] forState:(UIControlStateHighlighted+UIControlStateSelected)];
GarbageCalendar *garbageCalendar = [[GarbageCalendar alloc] initWithNibName:#"GarbageCalendar" bundle:nil];
[self presentModalViewController:garbageCalendar animated:YES];
}
And then I want to go home page from another viewController. But I didn' go home page viewController.
Create button on detail view controller, which calls something like this:
- (IBAction)goBack {
[self dismissModalViewControllerAnimated:YES];
}
If you want to keep your current UI design, based on modal view controllers, then I think you should ensure that your other view controllers have got a button that does the dismiss of the view. Say, e.g., a "Back" or "Done" button. When you click on that button, a delegate method is called that executes: [self dismissModalViewControllerAnimated:YES];
Look also at this document for more info, section "Dismissing a Modal View Controller".
If you would like to consider alternative approaches to your UI, you could look into using a UINavigationController, which would make your life a little bit easier with navigating back from one controller to another.