NavigationController is not popping the Pushed View on Back button - cocoa-touch

Having a simple Navigation Controller in place (starting the Navigation Based Application project) I created a new View with a XIB file.
on my HomeViewController (Home screen with all options as UIButton's I have:
#implementation HomeViewController
-(IBAction) optionChoosed:(UIButton *)button
{
NSString *msg = [NSString stringWithFormat:#"Button: %d", button.tag];
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"Hi" message:msg delegate:nil cancelButtonTitle:#"Go away" otherButtonTitles:nil];
switch (button.tag) {
case 13:
// Simple Search
[self loadSimpleSearch]; break;
default:
[alert show];
break;
}
[alert release];
}
-(void)loadSimpleSearch
{
SimpleSearchViewController *controller =
[[SimpleSearchViewController alloc] initWithNibName:#"SimpleSearchViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
witch works great!
it Pushes the View into the front of the stack!
Now, because in my 2nd view SimpleSearchViewController I have self.title = #"myTitle"; I get the Title in the NavigationBar as well the back button (as I have the same setting on the HomeViewController)
I thought that the NavigationViewController would handle the pop of the current view, but it does not.
What do I have to do, to pop the SimpleSearchViewController?
Where do I use [self.navigationController popViewControllerAnimated:YES];
as the view continues there, and ViewDidUnload is never called.
My idea was that this should be handle in the first ViewController, the HomeViewController but I have no idea what is the method I should hook to, and I read the documentation and I can't figure it out :-/
Any help is greatly appreciated, thank you.
HomeViewController
alt text http://cl.ly/XNS/Screen_shot_2010-04-21_at_22.38.51.png
SimpleSearchViewController
alt text http://cl.ly/YDw/Screen_shot_2010-04-21_at_22.40.00.png
SimpleSearchViewController after pressing the Back button
alt text http://cl.ly/XLO/Screen_shot_2010-04-21_at_22.40.21.png
To add the image from a comment that asks if HomeViewController as the root controller for the NavigationViewController

Not sure if this helps but when implementing navigationItem in code, if you don't call the super the popping functionality will not be present
-(UINavigationItem*) navigationItem
{
UINavigationItem* item = [super navigationItem];
item.title = #"search";
return item;
}

Related

Pop controller after back bar button is pressed

I have a UINavigationController ans a chain of 3 simple controllers. Each one has a button. When press a button a next controller is Pushed. ViewController1 -> ViewController2 -> ViewController3. When I push a back button on the 3rd view i want to move to the first view. Using of backBarButtonItem is obligatory. Here is the code for second controller:
#import "ViewController2.h"
static BOOL isBackButtonPressed;
#implementation ViewController2
- (void)viewDidLoad {
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:nil action:nil];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
if (isBackButtonPressed) {
[self.navigationController popViewControllerAnimated:YES];
} else {
isBackButtonPressed = YES;
}
[super viewWillAppear:animated];
}
#end
But when I press back button on the third view I return to the second view instead of the first view. Could you help me to return to the first view pressing back button on the third view.
I tried suggestions from answers but they don't help.
Adding a selector to backBarButtonItem doesn't help because it is never called.
Adding a [self.navigationController popToRootViewControllerAnimated:YES] in viewWillDisappear methos also doesn't work. I don't know why. I think that the actual problem is how backBarButtonItem works.
Any other suggestions?
The behaviour I try to achieve exists in the calendar on iPhone. When you rotate iPhone to landscape you get to the weeek view. Then go to the event details, and rotate to the portrait. When you press back button you will get to a day view not to a week view, so a controller with weekview is skipped.
After countless number of tries my solution was simply not use backBarButtonItem! As whatever i do it always goes to previous viewController instead of calling its selector
Instead I use only leftBarButtonItem for navigation, as it guarantees calling my action.
Here an example
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 27, 22)];
[backButton setImage:[UIImage imageNamed:#"backbutton"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
This certainly calls backButtonPressed action.. This works both for IOS 6 and 7
No need to register a new selector for the back button, just do:
-(void)viewWillDisappear{
if ( [self.navigationController.viewControllers containsObject:self] )
//It means that the view controller was popped (back button pressed or whatever)
//so we'll just pop one more view controller
[self.navigationController popViewControllerAnimated:NO];
}
in your ViewController3 viewWillDisappear method
Try using this in your third view controller, this way you check if you have pressed the back button and directly pop to the root view controller which as you stated is your first view controller.
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
[super viewWillDisappear:animated];
}
I had the same problem as you beofre and fixed it like this:
You can capture the back button on the ViewController3 and before poping the view, remove ViewController2 from the navigation stack like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:self action:#selector(customBackPressed:)];
}
-(void)customBackPressed:(id)sender {
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray: navigationController.viewControllers];
for (UIViewController *vc in viewControllers)
{
// If vc is ViewController2 type, remove it
}
navigationController.viewControllers = allViewControllers;
[self.navigationController popViewControllerAnimated:YES];
}
Because ViewController2 is not in the stack anymore, it will jump to ViewController1.
Also, if ViewController1 is the root view controller, you can just do:
[self.navigationController popToRootViewControllerAnimated:YES];

ModalViewController Without Dimming / Disabling current viewcontroller

I am displaying aNavController as a modalViewController in a specific frame CGRectMake(40,50, 400, 500). Which is working fine. Now I have a button in self (viewcontroller on which modalViewController is presented), on pressing that button I need to display some message on aNavController. But problem is when I am presenting a modalViewController. Whole screen area got dimmed/disabled. So, not able to touch/click that button in self.
Here is my code to present a view controller. I thought, I am missing something here. Please Help. Thanks in advance.
aNavController.modalPresentationStyle = UIModalPresentationFormSheet;
anavController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:aNavController animated:YES];
aNavController.view.superview.frame = CGRectMake(40,50, 400, 500);
presentModalViewController create a modal dialog. When modal view controller is up, users can't do any thing on parent view until the the modal view is dismissed.
The problem is you're instantiating a UIAlertView at the same time as the presentModalViewController call your modal view in UIAlertView's delegate method clickedButtonAtIndex.
Like so:
- (IBAction)clickedMyButton:(id)sender
{
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle: #"Title"
message: #"Message"
delegate:self
cancelButtonTitle:#"Close Button"
otherButtonTitles:#"Modal Button", #"Some Other Button", nil];
[alertView show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
NSLog(#"User Selected Cancel");
}
else if (buttonIndex == 1) {
NSLog(#"Modal Button Clicked");
aNavController.modalPresentationStyle = UIModalPresentationFormSheet;
anavController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:aNavController animated:YES];
aNavController.view.superview.frame = CGRectMake(40,50, 400, 500);
}else {
NSLog(#"Some Other Button Clicked");
}
}
Or, if you wish for your UIAlertView to show on top of your navigation controller, ignore the above and simply wait to call your alert until the navigation controllers - (void)viewDidAppear:(BOOL)animated method.
In addition, I recommend you change your frame to stay within the bounds of the screen unless absolutely necessary. ex: CGRectMake(40, 50, 320, 480);
Finally, I am able to do workaround the things which work same way as a UIModalPresentationFormSheet.
I added the aNavController as a subview to the [[UIApplication sharedApplication] keyWindow] and which solves my all the problems.
Thank you all for your comments.

Need a really simple navigation controller with a table view inside a tab bar controller

I have an app with a tab bar controller (2 tabs). In one tab view controller, a button leads to an alert window. I want one button of the alert window to call a table view containing possible answers. I want that table view to have a done button and a title. I think that means a navigation controller has to be used. But most everything I can find on navigation controllers assumes a much more complicated situation. Here's part of the alert window logic:
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 2) {
AnswersViewController *aVC = [[AnswersViewController alloc] init];
[self presentViewController:aVC
animated:YES
completion:NULL];
}
}
And AnswersViewController looks like this:
#interface AnswersViewController : UITableViewController
#end
#implementation AnswersViewController
- (id) init
{
self = [super initWithStyle:UITableViewStylePlain];
return self;
}
- (id) initWithStyle:(UITableViewStyle)style
{
return [self init];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[self view] setBackgroundColor:[UIColor redColor]];
}
#end
This code all works as expected (an empty red UITableView appears).
Two questions I guess: 1. Is there a simple modification to what I have that can give me a done button and title in my table view? 2. If I have to go to a navigation controller (probably), how can I make a bare-bones navigation controller with a done button and title and embed the table view within it? Oh, and I want to do this programatically. And I think I prefer the done button and title to be in the navigation bar, no tool bar desired. Thanks!
To get what you are looking for, you do need to use a UINavigationController. That will provide the UINavigationBar where you can display a title and also buttons.
To implement this with a UINavigationController, you want to do smoothing like this (assuming you are using ARC, so you don't need to worry about memory management):
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 2) {
AnswersViewController *aVC = [[AnswersViewController alloc] init];
//Make our done button
//Target is this same class, tapping the button will call dismissAnswersViewController:
aVC.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissAnswersViewController:)];
//Set the title of the view controller
aVC.title = #"Answers";
UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:aVC];
[self presentViewController:aNavigationController
animated:YES
completion:NULL];
}
}
Then you would also implement - (void)dismissAnswersViewController:(id)sender in the same class as the UIAlertView delegate method (based on the implementation I have here).
Hope this helps!

Is there a way to navigate to other views using a UIActionSheet?

Is there is a way to use an Action sheet to navigate to another view?
I have a button - "Go" and an Action sheet that asks "Do you want to proceed?" and in it "Yes" and "Cancel"
Can I can navigate to another view when pressing yes?
Yes, it is possible. To do this, the viewController that represents the UIActionSheet needs to adopt UIActionSheetDelegate. Upon dismissing the action sheet with either Yes or Cancel, - actionSheet:didDismissWithButtonIndex: method gets called, and from there you can navigate to another view or just ignore it.
References:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIActionSheet_Class/Reference/Reference.html#//apple_ref/occ/cl/UIActionSheet
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIModalViewDelegate_Protocol/UIActionSheetDelegate/UIActionSheetDelegate.html
Edit:
#interface MyViewController : UIViewController <UIActionSheetDelegate>
-(IBAction)showActionSheet:(id)sender;
#end
#implementation MyViewController
-(IBAction)showActionSheet:(id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Do you want to procced?" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"YES" otherButtonTitles:nil];
[actionSheet showInView:self.view];
}
-(void) actionSheet: (UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
switch (buttonIndex) {
case 0: {
// for illustration
// let's assume (1) you have a navigation controller
// (2) you are using storyboard
// (3) in the storyboard, you have a viewController with identifier MyChildViewControllerIdentifier
MyChildViewController *mcvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyChildViewControllerIdentifier"];
[self.navigationController pushViewController:mcvc animated:YES];
break;
}
default:
break;
}
}
P.S. I didn't run it, if there is any error, let me know to fix it.
Dismiss the actionsheet and then [self presentmodalviewcontroller:myview animated:yes]

Why can't I dismiss the previous view on a navigation stack?

This is probably going to be very obvious, but here goes. I have two ViewControllers, one that shows the details of a person with a [+] button that opens up another ViewController that manages the edits of the Person. When the person about to be deleted, an alert is presented and if OK is selected, the edit view should disappear and the DetailView should also disappear/transition back to the PersonsListView (not shown in code below).
I can get the EditView to dismiss, but I can't get the DetailView to dismiss as well.
Any help is appreciated.
The two ViewControllers:
#implementation PersonDetailViewController
...
- (void)editPerson
{
PersonDetailEditViewController *editView = [[PersonDetailEditViewController alloc] initWithTenant:self.person];
[editView setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:editView];
[self presentModalViewController:navController animated:YES];
}
...
#end
and
#implementation PersonDetailEditViewController
...
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1) {
[Person deletePerson:self.person];
NSError *error;
[self.person.managedObjectContext save:&error];
[[self navigationController] dismissModalViewControllerAnimated:YES];
// *** HERE I ALSO WANT TO DISMISS THE DETAILVIEW ***
}
}
...
#end
Assuming that PersonDetailViewController is presented in a navigation controller you should be able to do what you want by adding the following line:
[self.navigationController popViewControllerAnimated:YES];