I have a table view controller embedded in a navigation controller. The table cells are all static and selecting any of them will segue to another table view. When segue happened, the navigation bar shows 'Cancel' button for the new view, instead of 'Back' button.
I could add a back button in code like
- (void)viewDidLoad
{
self.navigationItem.backBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonSystemItemCancel
target:nil
action:nil];
self.navigationItem.leftBarButtonItem = self.navigationItem.backBarButtonItem;
}
But then the back button would be a rectangle shape, not the default back button shape which has an angle on the left edge. How to simply change the cancel button to a system back button?
Here is my code for segue from table cell to the next table view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row) {
case 0:
[self performSegueWithIdentifier:#"goToSecondTable" sender:self.tableView];
break;
/* and perform segue for other rows */
default:
break;
}
}
And there is nothing to do inside prepareForSegue.
Here is what the connections inspector showed
And Here is the connections for the 'Bar Button Item - Back'
The system provided back button should be the left bar button item by default without having to do anything (in code or in IB).
Remove the connection to the backBarButton in the Connections inspector. Remove the back bar button from the nav bar in IB. Remove the outlet to the back bar button in your code. Run your app, you should see a back bar button provided for you for free.
Why are you performing a segue in didselectrow instead of just pushing the viewcontroller?
try [self.navigationController pushViewController:YOURVIEWCONTROLLER];
that will make sure you have a back button. Also you must use a segue like that, make sure you are using a push segue to your next controller. It also looks like you created your own back button in the second viewController. You do not need to create one, as one will be created for you with the title of the previous viewcontroller. You can make sure it says back by changing self.title = #"Back" in the previous viewcontroller right before you push it.
Codes:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row) {
case 0:
self.title = #"Back";
[self.navigationController pushViewController:SECONDVC];
break;
/* and perform segue for other rows */
default:
break;
}
}
and in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.title = #"Whatever you want";
}
Customize the code if don't have default back button not working
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:myBackImage style: UIBarButtonItemStylePlain target:self action:someAction];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
Related
I have the following scenario:
A UIViewController is embedded inside a UINavigationViewController. The UIViewController has a UINavigationItem attached to it and has a UIContainerView as a subview. The UIContainerView has a embed segue to a UITableViewController with some static rows.
How can I make it so that when the user presses the Advanced row's
little arrow image on the right, the UIViewContainer's embed segue get
changed to reference another UITableViewController and have a Back
button on the UINavigationController so that the user can navigate
back to the original UITableViewController(with animations)?
Basically I want the user to be able to switch between Settings and Advanced Settings.
Things I have tried:
I tried having the container view have an embed segue to an UINavigationController. The first UITableViewController was set up to have a root-view controller segue relationship with the UINavigationController. I then made a show segue between the arrow (button) and another UITableViewController. This way I was able to transition between settings -> advanced settings -> settings, but I had double navigation bars. One is the bar I want, the white one on top, and the other is a gray one that appears below and where the back button is displayed. I want it to be a single navigation bar (the white one).
I have no problem using code to achieve what I want (Objective-C or Swift).
In the end I did it by code by adding the back button and dismissing the "Advanced Settings" view controller programmatically. I achieved this by created a show segue to the "Advanced Settings" UITableView. I then subclassed the UITableViewController:
#implementation AdvancedSettingsController
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:self action:#selector(goBack)];
self.navigationController = (UINavigationController*)self.parentViewController;
self.parentViewController.parentViewController.navigationItem.leftBarButtonItem = backButton;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Incomplete implementation, return the number of sections
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete implementation, return the number of rows
return 0;
}
-(IBAction)goBack
{
[self.navigationController popViewControllerAnimated:YES];
}
#end
I just wanted to have it be done using the Xcode builder to automate the process a bit, because I do not like the self.parentViewController.parentViewController thing, because if I change my controllers this will stop working and is a little hard to read.
End result (webm) - https://webmshare.com/m8Kne
In tableViewController i want to create a plus push button at "run time" in viewDidLoad of tableViewController to trigger another view.
I tried it as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
//
// create Button in Navigation
//
self.VC_AddToDoItem = [[GRF_VC_Add_To_Do_Item alloc] init];
[self.navigationController pushViewController:self.VC_AddToDoItem animated:NO];
UIBarButtonItem *bt_Plus = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self.VC_AddToDoItem action:#selector(add:)];
self.navigationItem.rightBarButtonItem = bt_Plus;
}
The result is that the table view is not displayed. Instead the view is displayed with link back to table view. If i press this link, table view is displayed. If i then press plus button then i get a crash.
What could be the problem?
Update:
I added the add method now in GRF_VC_Add_To_Do_Item. Crash is now gone. Method add is called. But i don't know what to do in this method to show the view, therefore this method is empty at the moment. If i press the "+" button, nothing happens. What call is necessary in add method to display view GRF_VC_Add_To_Do_Item?
In my RecordViewController, within the didSelectRowAtIndexPath I push a detailViewController (which inherits from UIViewController):
[[self navigationController] pushViewController:detailViewController animated:YES];
Once the DetailViewController appears I can see a Back navigationButton in the top left corner, which automatically pops the current view controller to get back to the previous ViewController.
Now I need to show a UIAlertView and ask the user, if the data should be saved or not.
And only when the user has made a decision, the current view controller should disappear.
My problem is if I put this code into viewWillDisappear, it is already too late. I can't stop the process while showing the UIAlertView. This needs to be intercepted the moment the user pressed the back button.
Is there a method I could override to achieve this?
Create a UIBarButtonItem:
UIBarButtonItem * backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: #"Back"
style: UIBarButtonItemStyleDone target: self action: #selector(onBackButtonTapped:)];
Assign it to left bar button item:
self.navigationItem.leftBarButtonItem = backBarButtonItem;
Implement onBackButtonTapped API:
- (void) onBackButtonTapped: (id) sender
{
// Display an UIAlertView
}
You may want to customize the back button. Please look into UIBarButtonItem for more details.
Instead of pushing a detail view controller, the usual way to gather data is to present one modally. That will give you 2 free spaces on the left and right of the (new) navigation bar to place a Save and Cancel button.
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
[self presentViewController:navigationController animated:YES completion:nil];
// detailViewController will have to set up buttons in its init
You can use a delegate protocol you create to handle save and cancel actions in the presenting (i.e. not the detail) view controller.
Overview: The idea is to have your own barbutton to intercept the backing out from the VC.
In your viewDidLoad you can do this
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStyleDone target:self action:#selector(Back:)];
self.navigationItem.backBarButtonItem = backButtonItem;
Then your Back: method can do this
-(void)Back:(id) sender
{
//Your code for showing AlertView with delegate as self. Remember to conform to the UIAlertViewDelegate protocol.
}
Then put your save functionality in
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (selected buttonIndex is the one for saving data)
{
//save your data
}
//popViewController
}
- (void)createBarButtons
{
UIBarButtonItem *myCheckButton = [[UIBarButtonItem alloc] initWithTitle:#"Check Records" style:UIBarButtonItemStylePlain target:self action:#selector(checkRecordsAction)];
UIBarButtonItem *mySaveButton = [[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStylePlain target:self action:#selector(saveAction)];
[mySaveButton setTintColor:[UIColor colorWithRed:34.0/255.0 green:97.0/255.0 blue:221.0/255.0 alpha:1]];
NSArray *myButtonArray = [[NSArray alloc]initWithObjects:mySaveButton, myCheckButton,nil];
self.navigationItem.rightBarButtonItems = myButtonArray;
}
I dont know if this question falls under too localized category. But help me out here.So like you see i have created two bar button items. Save is just saving the data onto CoreData,works just fine. But the check records should launch a new UITableviewcontroller.
- (void)checkRecordsAction
{
NSLog(#"the new stack action");
ITMSyncRecordsTVC *syncRecords = [[ITMSyncRecordsTVC alloc]init];
// [self presentViewController:syncRecords animated:YES completion:^{
// self.navigationController.view.superview.bounds = CGRectMake(0, 0, 250, 250);}];
[self.navigationController pushViewController:syncRecords animated:YES];
}
ITMSyncRecordsTVC is a TableViewController with a "Back" button on it.So when i click the check records it launches a tableview controller but no values in it and it does not show the "Back" bar button i put on it. Until now i have been using segues and storyboards just fine. But how do i launch a new view controller without them i dont know. My first leap into ios is ios6. I am missing something I dont know. So let me know how to call/launch a new TableViewController. In android we had intents that did the trick. Please let me know if you need more information. Thanks...
EDIT: So i edited my checkRecordsAction code.
EDIT :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ITMOrdersVC *ordersVC = [[ITMOrdersVC alloc]init];
NSLog(#"at line 188 %d",indexPath.row);
if(indexPath.row < self.salesOrdersArray.count)
{
ordersVC.salesOrder = [self.salesOrdersArray objectAtIndex:indexPath.row];
NSLog(#"the sales purchase order number is %#",ordersVC.salesOrder.purchaseOrderNumber);
NSLog(#"done - 140");
[self.navigationController pushViewController:ordersVC animated:YES];
}
}
So on selecting a row on ITMSyncRecordsTVC table view controller it does the above. I get a new ITMOrdersVC screen with "Back" bar button at the left and 2 dynamically generated bar buttons.I get the 2 dynamically generated bar buttons but not the back. I thought once i click the row it will "go back" to previous screen to which i am passing the salesOrder object. My next step was to check if i get the salesorder object from the TVC then load it. So basically 2 screens only. First screen (save,check sync records). Second screen click a.) back(go to first screen..do nothing) or b.)click a row in second screen and populate first screen without the bar button.If it is not clear please ask me.
To launch a new table view controller using storyboards, you want to:
Have your main scene embedded in a navigation controller.
You want to have a push segue from the main controller to the second one. So control-drag from the view controller icon (in the bar below the main scene) to the next scene:
It should then look like (note the appearance of the navigation bar in the destination table view):
Then select the segue and give it a "storyboard identifier":
And now, your code to transition to that scene would look like:
The checkRecordsAction:
- (void)checkRecordsAction
{
NSLog(#"the new stack action");
[self performSegueWithIdentifier:#"PushSegue" sender:self];
}
Update:
By the way, in the interest of full disclosure, there's an alternative to the push segue. If you give that next scene, itself, a "storyboard id", you can use the following code (obviously replacing "insertNextScenesStoryboardIdHere" with the identifier you give your next scene:
- (void)checkRecordsAction
{
NSLog(#"the new stack action");
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"insertNextScenesStoryboardIdHere"];
[self.navigationController pushViewController:controller animated:YES];
}
I personally don't like this as much, as your storyboard now has a scene floating out there without any segue, your code now has dictated the nature of the transition between view controllers vs having everything in the storyboard, etc., but it is another approach.
I'm trying to create a transition between two scenes, this is a dumbed down version of what I have in my production code :
Both are ViewController, the left one has a TableView inside it and when clicked it should transition to the right hand scene, passing along data from whatever cell was clicked.
Currently, with a modal segue I can tap the cell and it transitions correctly, however, I can't figure out how to place a back button onto the nav bar.
I'm transitioning from the cell to the 2nd view controller like so :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toSecond" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toSecond"])
{
NSLog(#"Preparing segue for toSecond, setting some data on target scene");
NSIndexPath *path = [self.theTableView indexPathForSelectedRow];
MyData * myData = [myDataArray objectAtIndex:path.row];
// Obtain handles on the current and destination controllers
FirstController * startingViewController;
SecondController * destinationController;
startingViewController = (FirstController * ) segue.sourceViewController;
destinationController = (SecondController * ) segue.destinationViewController;
destinationController.someData = myData;
}
}
On the SecondController, I've tried amending the viewDidLoad method to programatically include a back button item as suggested in this previous SO question:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIBarButtonItem * back = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStylePlain
target:nil
action:nil];
[self.navigationItem setBackBarButtonItem:back];
}
So my question is, how can I get a back button onto that nav bar? Something like this :
Thanks
One (easy) way, using the storyboard, is to embed a navigation controller into your view that has the table. Make sure the correct view, the one with the table, is highlighted and then go to Editor > Embed In > Navigation Controller.
The back button will automatically be there when you click a row of your table. Like Dan said, make sure it is a push segue between the 2 views in your picture.
You will not be able to add a back looking button to a modally presented ViewController easily
If you just want to add a normal button to the left side of the bar, do the following
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonSystemItemDone target:nil action:nil];
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:#"Title"];
item.leftBarButtonItem = rightButton;
item.hidesBackButton = YES;
[self.yourNavigationBar pushNavigationItem:item animated:NO];
If you insist of presenting your view modally and still want a back button style you could use
Three20
In place of where you have the view with the table, us a UINavigationController don't delete the view with the table, just in place of where you segue to it, or if it was the root controller, use the nav con. Make the view with the table the root view controller of the navigation controller, and then simply use a push segue instead of a modal segue, and you should automatically get the back button
The best way is to just use a UINavigationController as the parent of your table view controller, and use a push segue instead of a modal segue.
You can create two instances of UINavigationItem and tell the UINavigationBar about them by setting the bar's items property. Instance 0 represents the table view controller.
You can create a UIButton with type 101 (the undocumented back button type), and wrap it in a UIBarButtonItem using initWithCustomView:.