Editing a Textfield in a Detail Controller After Clicking a Cell - objective-c

I'm having a little trouble editing names in a cell after a user inputted them in. Essentially they click on a button 'add name' which takes them to a detail Controller and they enter in their first and last name and click 'done'. That delegates back to the master Controller and the cell is updated to show that name.
What I'd like is for them to be able to input many names and then click on a cell afterwards which takes them back to the data they've already inputted (which is stored in an NSMutableArray called 'entry').
-(void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
self.detailView.firstNameField.text = [[self.entry objectAtIndex:indexPath.row]firstName]];
NSLog(#"%#",[[self.entry objectAtIndex:indexPath.row]firstName]);
NSLog(#"%#", self.detailView.firstNameField.text);
}
The first NSLog shows the name just fine, but the second comes back as "null" and the textfields are blank, ready to have a new name added...All and any help is greatly appreciated :)

I think you are doing this the wrong way the way it works, you dont set values directly to the UITextfield , Instead you pass it a NSString which would be a property of your model.
That is the whole point of MVC the data should not interact with the UI directly.
In your MasterViewController you need to import the DetailViewController header:
#import "DetailViewController.h"
perform the segue in the didSelectedRow:
-(void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
[self performSegueWithIdentifier:#"detailSegue" sender:sender];
}
implement the segue deleguate and set its firstName property (NSSrting)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if([segue.identifier isEqualToString:#"detailSegue"]){
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController*detailView = (DetailViewController*)segue.destinationViewController;
detailVC.firstName =[[self.entry objectAtIndex:indexPath.row]firstName]];
NSLog(#"%#",[[self.entry objectAtIndex:indexPath.row]firstName]);
}
}
then in viewDidLoad of DetailView you would assign the firstName string to your UItextfield text property:
_firstNameField.text = self.firstName;

Related

Can not get correct value of indexPath.row in prepareForSegue when UILabel or UIImageView in a cell is tapped,

I am developing iOS App with UITableView.
When UILabel or UIImageView in a cell is tapped, segue is called and move to another screen.
I am writing down the following code, however I can not get correct value of indexPath.row in prepareForSegue.The value is always 0.
- (void)goToDetail: (UITapGestureRecognizer *)sender{
[self performSegueWithIdentifier:#"showDetail" sender:self];
NSLog(#"aaa");
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController *detailViewController = [segue destinationViewController];
detailViewController.article = _article[indexPath.row];
NSLog(#"bbb%d",indexPath.row);
}
}
NSLog says following.
2014-08-08 17:50:23.267 eleventhtest[27374:60b] bbb0
2014-08-08 17:50:23.269 eleventhtest[27374:60b] aaa
Could you tell me how to solve this problem?
As just one of the possible options, you can set tag for label/imageView and gesture recognizer when you are creating cells. The tag would be the same as the indexPath.row.
In goToDetail method you can assign this tag to some internal property, and then use it in prepareForSegue.
If you're calling goToDetail in the didSelectRowAtIndexPath method AND have a segue wired from the prototype row to your detailViewController, then the segue fires BEFORE the goToDetail method.
Solution: segue directly from your view controller to your detailViewController and call [self performSegueWithIdentifier:#"showDetail" sender:self]; in the didSelectRowAtIndexPath method.
Also you can create a NSIndexPath property and set the value in the didSelectRowAtIndexPath method, the segue would then use that property instead of trying to access the [self.tableView indexPathForSelectedRow].

update a UILabel when the cell in UITableView is selected

A really simple question here. I have a label on one view and a UITableView on the previous view. I have got a segue triggered when the user selects the row and I want the label to be updated with the text from that row. Here's one example, the code is pretty obvious.
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *countrySelection;
switch (indexPath.section) {
case kFirstSection:
countrySelection = [[NSString alloc]
initWithFormat:#"The country you have chosen is %#",
[self.MyCountries objectAtIndex: indexPath.row]];
[self performSegueWithIdentifier:#"doneResults" sender:self];
self.countryResult.text = countrySelection;
break;
The label isn't updated and I just don't know what should be done.
Thanks in advance!
These kind of things really need to be set on the View Controller that owns them. Use a public property to pass the value of the selected country to that view controller as outlined below:
First, create a property called something like:
#property(non atomic,strong) NSString *countryChosen;
in the destination View Controller, and make sure to #synthesize it
No reason to create another property for the IndexPath. Just use
// Pass along the indexPath to the segue prepareForSegue method, since sender can be any object
[self performSegueWithIdentifier:#"doneResults" sender:indexPath];
in the didSelectRowAtIndexPath method.
Then in the prepareForSegueMethod:
MyDestinationViewController *mdvc = segue.destinationViewController;
NSIndexPath *indexPath = (NSIndexPath *)sender;
mdvc.countryChosen = [self.MyCountries objectAtIndex: indexPath.row]];
On the viewDidLoad event of the Destination VC, just use:
self.countryResult.text = countryChosen;
* EDIT *
To deal with a datasource that has multiple sections, just use the same logic that you have in the cellForRowAtIndexPath.
NSDictionary *selRow = [[self.countriesIndexArray valueForKey:[[[self.countriesIndexArray allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:sindexPath.row];
Change this to suit your needs, but basically you are implementing the same logic that you would to display a cell, except you are specifying the indexPath (both section and row) that you want.
Then something like the following to set that property on the destination VC:
self.countryResult.text = [selRow valueForKey#"Country"];
In your current view controller create a new property for the indexPath of the cell the user selected, like this:
#property(strong,nonatomic) NSIndexPath *path;
#synthesize it and then when a user selects a row, set it by using
self.path = indexPath;
When you perform a segue, it will always call
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
So what you can do now when prepareForSegue: gets called, is the following:
/* if this is not the only segue you are performing you want to check on the identifier first to make sure this is the correct segue */
NSString *countrySelection = [[NSString alloc]
initWithFormat:#"The country you have chosen is %#",
[self.MyCountries objectAtIndex: self.path.row]];
segue.destinationViewController.countryResult.text = countrySelection;
/* after creating the text, set the indexPath to nil again because you don't have to keep it around anymore */
self.path = nil;
For this to work the view controller you want to show after selecting the cell must have a property for the UILabel, on which you are trying to set the text.

Objective-C, Storyboard: instantiateViewControllerWithIdentifier returns nil

I have a UITableViewController with a storyboard push segue linking from the prototype cell to a detail page, a regular old UIViewController. In the storyboard, the detail ViewController has an identifier, and the segue has an identifier which is the same as the detail identifier except that the first letter is lowercase. Furthermore, the detail ViewController has a "custom class" (AttractionDetailViewController) selected in the class pulldown.
Doesn't work. The problem is that instantiateViewControllerWithIdentifier:#"AttractionDetails returns nil.
Relevant code. First the prepareForSegue method which the debugger has never entered.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"attractionDetails"])
{
AttractionDetailViewController *attrDetailVC = [segue destinationViewController];
}
}
Instead it goes into this method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//AttractionDetailViewController *attrDetailVC = [[AttractionDetailViewController alloc] init];
AttractionDetailViewController *attrDetailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"AttractionDetails"];
NSIndexPath *selIndexPath = [self.tableView indexPathForSelectedRow];
attrDetailVC.theAttraction = [attractions objectAtIndex:indexPath.row];
[self.navigationController pushViewController:attrDetailVC animated:YES];
}
Since instantiateViewControllerWithIdentifier returns nil it throws an exception of course. The really interesting thing is, if I use the alloc init line instead, it works, but the screen is all black.
Anyway, I've read up about this and tried a few different things and I'm still stymied. Does anyone have any suggestions?
The problem is that you didn't instantiate your master view controller (the UITableViewController) from the storyboard, so its storyboard property is nil.

viewDidLoad being called before didSelectRowAtIndexPath

I have
viewDidLoad (of next tableView) being called before didSelectRowAtIndexPath (of current tableView).
I am using a 'singleton' to hold my model and up to now this has been working quite well. I am trying to pass on the row selected in the current table in didSelectRowAtIndexPath to the destination tableview (they are linked by a segue in storyboard) by setting by the variable in didSelectRowAtIndexPath.
However the viewDidLoad of the destination tableview is called before I can set the row number. didSelectRowAtIndexPath is called later but by then I have set up my data (wrongly) for the destination table.
When using segues you do not need didSelectRowAtIndexPath:. You just link the segue to the cell in IB. In the code just use this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
CustomViewController *newVC = [segue destinationViewController];
newVC.property = theDataYouWantToPass;
}
You can retrieve your row through the sender variable that is passed.
UITableViewCell *cell = (UITableViewCell *) sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSInteger myRow = indexPath.row;

How to use a UITableView to return a value

How would I use a tableView as a value selector?
So I have a series of input fields and what I want is when you select a cetian field it opens a tableview of options that you can pick from as a value for that field.
Upon selecting an option it returns to the previous View with the selected value filling that field.
This is what I do, similar to the Settings > General > International > Language table view in the iPhone/iPod.
The user can tap a row and a check mark will appear. The view is dismissed when "Done" or "Cancel" is tapped.
First, create a UITableViewController that will display your options. Have a toolbar on the top with a Cancel and Done button. Also have these properties:
SEL selector; // will hold the selector to be invoked when the user taps the Done button
id target; // target for the selector
NSUInteger selectedRow; // hold the last selected row
This view will be presented with the presentModalViewController:animated: method so it appears from the bottom of the screen. You could present it in any other way, but it seems kind of standard across iPhone applications.
Before presenting the view, set target and selector so a method will be called when the user taps the "Done" button.
Now, in your newly created UITableViewController you can implement the thetableView:didSelectRowAtIndexPath:` method as:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark; // show checkmark
[cell setSelected:NO animated:YES]; // deselect row so it doesn't remain selected
cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:selectedRow inSection:0]];
cell.accessoryType = UITableViewCellAccessoryNone; // remove check from previously selected row
selectedRow = indexPath.row; // remember the newly selected row
}
Also implement cancel and done methods for the toolbar buttons:
- (IBAction)done:(UIBarButtonItem *)item
{
[target performSelector:selector withObject:[stringArray objectAtIndex:selectedRow]];
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)cancel:(UIBarButtonItem *)item
{
[self dismissModalViewControllerAnimated:YES];
}
You should use UITableViewDelegate's tableView:didSelectRowAtIndexPath:, remember the value somewhere in another object (a share instance/singleton maybe? - depending on your architecture) and then dismiss this table view.
I implemented a ViewController for Date pick.
I create a protocol to return the date picked to the previous view.
#protocol DataViewDelegate
#optional
- (void)dataViewControllerDidFinish:(NSDate*)dateSelected;
#end
...
- (void) viewDidDisappear:(BOOL)animated
{
if ([ (id)(self.delegate) respondsToSelector:#selector(dataViewControllerDidFinish:)])
{
[self.delegate dataViewControllerDidFinish:self.data];
}
[super viewDidDisappear:animated];
}
In the picker view you can use the
tableView:didSelectRowAtIndexPath:
to select the row you want. Here i set the data property.
The previous view is the delegate for the protocol.