viewDidLoad being called before didSelectRowAtIndexPath - objective-c

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;

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].

Editing a Textfield in a Detail Controller After Clicking a Cell

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;

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.

NSIndexPath of UITableViewCell subview?

Is there any was to get the NSIndexPath of a UITableViewCell's contentView subview?
In my case, I have a table view in which each row is split into 3 buttons. I can use the button tag to keep track of which column it is but I also need to know what row of the table view it is a subview of (when the user taps the button).
Because the button blocks the entire actual table view row, didSelectRowAtIndexPath is never called (which is expected), so I can't get it that way.
I have tried just [thisButton superview] but that doesn't seem to work. Any thoughts?
I assume the button is sending its UIControlEventTouchUpInside event message to your view controller? Then you could do something like:
- (void)buttonPressed:(UIButton *)button
{
CGPoint location = [button.superview convertPoint:button.center toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
[self doSomethingWithRowAtIndexPath:indexPath];
}
One possible option is when making the cell for index path you can assign the tag to be 3 * row_number + <the tag number>. then just divide the tag by 3 to get the row and %3 to get the button.
You don't need to call button.superview. You can go straight from any subview in the tree right to the table view
Objective-C
- (NSIndexPath *)indexPathForCellContainingView:(UIView *)view inTableView:(UITableView *)tableView {
CGPoint viewCenterRelativeToTableview = [tableView convertPoint:CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)) fromView:view];
NSIndexPath *cellIndexPath = [tableView indexPathForRowAtPoint:viewCenterRelativeToTableview];
return cellIndexPath
}
Swift
func indexPathForCellContainingView(view: UIView, inTableView tableView:UITableView) -> NSIndexPath? {
let viewCenterRelativeToTableview = tableView.convertPoint(CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)), fromView:view)
return tableView.indexPathForRowAtPoint(viewCenterRelativeToTableview)
}

Return NSIndexPath from custom cell ? (UITableView)

I need to get the NSIndexPath for a custom cell in a UITableView. Here's the problem: I send a NSNotification from my custom cell to my UITableViewController when editingDidBegin gets called from a UITextField in my custom cell. In my UITableViewController, I resize the UITableView when the UITextField began editing, and then want the table view to scroll to the Cell in which the UITextField is first responder. But I can't figure out how to return the indexPath of the cell where the UITextField is being edited. I have tried so many ways, but its still not working. One way is this: in my cstomCell class i select the row using [self setSelected:YES] and in my TV controller then if I NSLog the row of [self.tableV indexPathForSelectedRow] it always returns 0 even though its always not 0.
Just give the cell a value for its tag property. Then you can get that cell by calling this
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:tagValue];
then once you have the cell you can get the NSIndexPath like this
NSIndexPath *indexPath = [self.tableView indexPathForCell:nextResponderCell];
Since you have a UITextField in your custom cell you can place cell.textField.delegate = self; in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
data source method. That way you will not have to setup a NSNotification in the Custom Cell. Also in this same method you can tag both your cells text field like this cell.textField.tag = indexPath.row; and the cell like this cell.tag = indexPath.row;
Now that you have set the UITextField delegate, you can now place this method in your UITableViewController class
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CustomCell *cell = (CustomCell *)[self.tableView viewWithTag:textField.tag];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
}
The above UITextField delegate method should get you the indexPath for the cell you have currently selected.
try this code..
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableView *tableView;
UITableViewCell* superViewCell = [self returnSuperCellViewOfTextField:textField];
if(superViewCell)[tableView scrollToRowAtIndexPath:[tableView indexPathForCell:superViewCell] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
-(id)returnSuperCellViewOfTextField:(id)viewToCheck
{
id superView = [viewToCheck superview];
if([viewToCheck isKindOfClass:[UITableViewCell class]] && superView)return superView;
else if(([viewToCheck respondsToSelector:#selector(superview)] && superView)) return [self returnSuperCellViewOfTextField:superView];
return nil;
}
Does this (your answer that you gave before below)still works if you have multiple texfields in each cell?:
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:tagValue];
then once you have the cell you can get the NSIndexPath like this
NSIndexPath *indexPath = [self.tableView indexPathForCell:nextResponderCell];
Since you have a UITextField in your custom cell you can place cell.textField.delegate = self; in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
data source method. Also in this same method you can tag both your cells text field like this cell.textField.tag = indexPath.row; and the cell like this cell.tag = indexPath.row;
Now that you have set the UITextField delegate, you can now place this method in your UITableViewController class
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CustomCell *cell = (CustomCell *)[self.tableView viewWithTag:textField.tag];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
I have a tableview with 8 cells, and in each cell i have 6 texfields, and also the tableview is dynamic, so the user can insert more cells...but i´m having trouble in how to save the text the user enters in each texfield. Is it possible that with "cell.textField.tag = indexPath.row", the controller identify´s the texfield i´m in?
I like to tag my cells like so:
/* Create a tag based on the section and row of a cell(used to retrive the cell for the beginEditingNextCell method. */
-(NSInteger)tagForIndexPath:(NSIndexPath*)path {
return path.section * 1000 + path.row + 1;
}
The other way is to pass the beginEditing data using a custom delegate, and include a cell model, which you can then use to figure out your path depending on how you are storing data.
Check out my github project, it has the code to handle this case, as well as resizing for the keyboard and tabbing to the next editable cell: https://github.com/andrewzimmer906/XCell