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.
Related
I am trying to solve this issue regarding a UITableView cell being off screen, or outside the visible area.
Within my tableview cells I have a UITextField which I am able to parse easily using the code below. However I find that that for the cells that are not visible it returns a NULL value.
I am guessing this is a feature to improve memory usage but is there anyway to turn it off? Or if not is there a work around?
InputCell *inputCell = (InputCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
UITextField *cellContent = (UITextField *)[inputCell.textInput viewWithTag:0];
NSLog(#"Cell Content: %#" , cellContent.text);
Thanks and thanks again!
Views need models, especially table views. A model is some object (often a group of objects in collection classes) that represents the state of your app. A table view requires an array. The datasource protocol asks you to describe that array. Since tableview cells are part of the view, they shouldn't be relied upon to keep the state of your app. That's up to you as follows:
In your vc's private interface:
#property(strong, nonatomic) NSMutableArray *myDatasource;
Early, like in view did load:
myDatasource = [NSMutableArray array];
// fill it with strings
In numberOfRowsInSection...:
return self.myDatasource.count;
In cellForRowAtIndexPath:
cellContent.text = self.myDatasource[indexPath.row];
Make the vc your textField's delegate and implement:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *candidateString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSIndexPath *indexPath = [self indexPathWithSubview:textField];
self.myDatasource replaceObjectAtIndex:[indexPath.row] withObject: candidateString];
return YES;
}
This helper finds the indexPath of any textField (any subview) of any cell:
- (NSIndexPath *)indexPathWithSubview:(UIView *)subview {
while (![subview isKindOfClass:[UITableViewCell self]] && subview) {
subview = subview.superview;
}
return [self.tableView indexPathForCell:(UITableViewCell *)subview];
}
It looks like a lot, but not so bad when you get used to it. The pattern is always - think of the objects that describe the state of your app (model). Think of views that best describe and manipulate that state (view). Make your view controllers (controllers) (a) notice model changes and change the views accordingly, and (b) hear about user actions from the views and update the model.
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;
I am developing my first app in objective-c. In first view controller I am able to generate UITableview with dynamic prototype cells containing accessory button. When I click on the accessory button, it will navigate to next view controller, which would also having dynamic UITableviewcells with (Custom button)Select as accessory button. But, when I click on the specific select button, corresponding cell contents should be updated in the cell(In first screen) for which accessory button is clicked. I am able to get the details from the second screen to first screen. But, my custom cell in the first screen is not updating?
Please help whether there is any pre-defined function, which I've not found in the reference doc.
Thankyou in advance..
There is a solution algoritm come from beginning ios 5 development.
1.Take the indexPath in prepareSegue
// prepare selection info
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
2.Send that to second view controller
UIViewController *destination = segue.destinationViewController;
[destination setValue:self forKey:#"delegate"];
id object = [self.tasks objectAtIndex:indexPath.row];
NSDictionary *selection = [NSDictionary dictionaryWithObjectsAndKeys:indexPath,#"indexPath",object,#"object", nil];
[destination setValue:selection forKey:#"selection"];
3.The second view controller later will send back the indexPath to the first controller
// prepare selection info back
NSIndexPath *indexPath = [_selection objectForKey:#"indexPath"];
id object = _textView.text;
NSDictionary *editedSelection = [NSDictionary dictionaryWithObjectsAndKeys:indexPath, #"indexPath", object, #"object", nil];
[_delegate setValue:editedSelection forKey:#"editedSelection"];
4.So in the getter method of first view controller which used to take the indexPath back from the secomd view controller, you take back the table cell by indexPath and change its value
#pragma mark - Call back by destination view
-(void)setEditedSelection:(NSDictionary *)dict{
NSIndexPath *indexPath = [dict objectForKey:#"indexPath"];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
....
}
I was wondering, is there any methods to retrieve a specific cell from an UITableView?
For example, from where I am I can access my UITableView, so I would like to call something like cellForRowAtInteger:3 and that would return a cell so I could manipulate it.
Any ideas?
Thanks!
You can use -cellForRowAtIndexPath: method from UITableView. But remember that it will return nil if the cell is not visible.
Make your own function to create an NSIndexPath from your NSInteger.
-(UITableViewCell *) getCellAt:(NSInteger)index{
NSUInteger indexArr[] = {0,index}; // First one is the section, second the row
NSIndexPath *myPath = [NSIndexPath indexPathWithIndexes:indexArr length:2];
return [self tableView:[self tableView] cellForRowAtIndexPath:myPath];
}
You can then call it anywhere using:
UITableViewCell *hello = [self getCellAt:4]; // replace 4 with row number
If you have more than one section, then you need to change the 0 accordingly to the section.
I have a UITableView that once a cell is clicked, it pushes tableViewB, which contains customcells. These cells contain TextFields. Once the user does any updating, they click "Save", which then pops tableViewB and goes to the first UITableView. I would like to get all of the UITextField values from tableViewB when Save is clicked. What is the best way to go about doing that?
The problem is that I need to loop through all UITableView cells. I'm not sure how that is done or if it is even a good approach. Just looking for help on what is a good technique here.
in your tableViewB header, declare:
NSMutableArray *stringArray;
and in the implementation:
- (id) init { //whatever your tableViewB initializer looks like
if ([self = [super init]) {
//oldData is an NSArray containing the initial values for each text field in order
stringArray = [[NSMutableArray alloc] initWithArray:oldData];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
//Making the cell
[cell.textfield addTarget:self action:#selector(updateField:) forControlEvents:UIControlEventValueChanged];
....
//Setting up the cell
cell.textfield.tag = indexPath.row;
cell.textfield.text = [stringArray objectAtIndex:indexPath.row];
return cell;
}
- (void) updateField:(UITextField *)source {
NSString *text = source.text;
[stringArray replaceObjectAtIndex:source.tag withObject:text];
}
- (void) dealloc {
[stringArray release];
}
There are several ways you can choose to get your data back to the original table view, either by delegate, or by having the stringArray declared as a variable passed in to the tableViewB initializer rather than allocated there.
You should be aware that, in general, there are only about as many cell allocated as displayed on the screen. The cells that are not visible are actually not persistent but only get created when tableView:cellForRowAtIndexPath: is called. I suggest you create an array to cache the contents of all the text fields and which gets updated whenever a user leaves a text field (e.g. the textField:shouldEndEditing: method or something like that) is called.
If I understand your question - id each cell numerically and reference them in an array/climb the array to loop through them