Getting a specific UITableViewCell out of a UITableView - cocoa-touch

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.

Related

UITableView accessoryType disappears in last half of UITableView

I have an iPad app (XCode 4.6, ARC, Storyboards, iOS 6.2.3). I have a UIPopover with a UITableView that has 21 rows in it. I can set the accessoryType in all of the rows randomly, but only in the first 12 rows does the accessoryType setting (checkmark) persist so it can be examined in another method and processed. I don't see any difference between the first 12 rows and the last 9 rows. The UITableView is scrollable, so to get to the rows after the 11th row, you have to scroll to the bottom
Here is the code to set the accessoryType:
#pragma mark didSelectRowAtIndexPath
- (void) tableView:(UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
// get the cell that was selected
UITableViewCell *theCell = [tableView cellForRowAtIndexPath:indexPath];
if(theCell.accessoryType != UITableViewCellAccessoryCheckmark)
theCell.accessoryType = UITableViewCellAccessoryCheckmark;
else
theCell.accessoryType = UITableViewCellAccessoryNone;
}
Here is the code where I check the accessoryType and process it:
-(void) moveServices { // (moves checked tableViewRows to services tableview)
NSMutableString *result = [NSMutableString string];
for (int i = 0; i < [servicesArray count]; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
[tvServices scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
UITableViewCell *cell = [tvServices cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
[result appendFormat:#"%#, ",cell.textLabel.text];
NSLog(#"\n\ni: %d\ncell.accessoryType: %d\ncell.textLabel: %#",i,cell.accessoryType, cell.textLabel);
}
}
if (result.length > 2) { // move to text box in main menu
storeServices =[result substringToIndex:[result length] - 2];
}
}
It looks like you're mixing the notion of "data source" and the contents of the cells in the table. Don't do that -- keep your data source (whether or not a particular row in the table should display a checkmark based on your program logic) separate from the settings of particular cells (whether or not a particular cell displays a checkmark). Then in cellForRowAtIndexPath, you build the cell to match your data source's current settings. The reason is that UITableView reuses cell instances based on which rows are visible on-screen (and it's just good MVC design).
In your case you should keep an NSMutableArray property in your class that records the settings for the entire table, and use the value from that array in cellForRowAtIndexPath to set up that particular cell. Then the other "business logic" methods in your controller use the array property to query your model state instead of the cell settings (which are part of the view and should be independent from the data model).

Add selected Cell text label to array when button is clicked

I have an app right now that has a table view with multiple cells that were loaded from an array. I delimited the text in a text view to separate the text into components that then were added to an array. From there I set the text label of each cell to each component in the array. So I have something that looks like this...
And I want to be able to select a cell and it highlights the cell, then I want to be able to click one of the buttons on the right. When I click a button it takes the text label of that cell and stores it in an array as a component.
I don't know how I would go about writing the code for "take the text label of the selected cell and store it as a component." Is there a way to detect if the cell is selected?
You'll want to use a NSMutableArray to achieve this because you can add and remove objects from it on the fly:
- (void)viewDidLoad
{
[super viewDidLoad];
myMutableArray = [[NSMutableArray alloc] init];
}
- (void)myButtonClicked
{
NSMutableArray *myMutableArray;
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]];
if ([myMutableArray containsObject:cell.textLabel.text]) {
[myMutableArray removeObject:cell.textLabel.text];
}else{
[myMutableArray addObject:cell.textLabel.text];
}
}
An even better approach is to take the text from your array that provides data to the table view and put that in the other array. I'll call them sourceArray and destinationArray.
- (IBAction)buttonAction:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *string = [self.sourceArray objectAtIndex:indexPath.row];
[self.destinationArray addObject:string];
}
I suspect though that the indexPathForSelectedRow method was the one you were looking for. If you still need to go with the label text, modify the handler as shown:
- (IBAction)buttonAction:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString *string = selectedCell.textLabel.text;
[self.destinationArray addObject:string];
}
Hope this helps!

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)
}

adding accessorytype without using the delegates

is there a way to add cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
to each row in a table without using delegate tableView functions?(the reason for this is a bit complicated..)
im thinking on stuff like
for(UITableViewCell *cell in TheTable){
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
but this wont work..
any ideas?
ty in advance!
You will never have access to all the cells from the UITableView object. You can access the visible cells like #Jhaliya mentioned. Of course your best approach here is to have a BOOL instance variable to keep track of whether cells have the detail disclosure button and flip them.
BOOL isDetailDisclosureAccessoryVisible;
In tableView:cellForRowAtIndexPath:,
if ( isDetailDisclosureAccessoryVisible ) {
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
When you want to make them available,
isDetailDisclosureAccessoryVisible = YES;
[TheTable reloadRowsAtIndexPaths:[TheTable indexPathsForVisibleRows]
WithRowAnimation:UITableViewRowAnimationNone]; //Choose an appropriate animation here.
You could do it by accessing the UITableViewCell in an functions defined by you.
There are number of function in UITableView which you could use to access the UITableViewCell outside the delegate functions.
- (NSArray *)visibleCells;
- (NSArray *)indexPathsForRowsInRect:(CGRect)rect;
- (NSArray *)indexPathsForVisibleRows;
To access a cell for a given indexPath use the below method.
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
For more read UITabelView apple doc.