iPad: Iterate over every cell in a UITableView? - ipad

iPad: Iterate over every cell in a UITableView?

for (int section = 0; section < [tableView numberOfSections]; section++) {
for (int row = 0; row < [tableView numberOfRowsInSection:section]; row++) {
NSIndexPath* cellPath = [NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell* cell = [tableView cellForRowAtIndexPath:cellPath];
//do stuff with 'cell'
}
}

To iterate over every visible cell in a UITableView:
for (UITableViewCell *cell in self.tableView.visibleCells) {
NSIndexPath *cellIndexPath = [self.tableView indexPathForCell:cell];
(edited to better state answer and hope that this is indexed more accurately for search results with the intention of saving others more time in the future)

(This builds on aroths answer.)
I like to define this as a category to UITableView so it's available everywhere.
(As mentioned a few times, you should be sure you really want to iterate over the cells themselves. For example: I use this to clear the UITableViewAccessoryCheckmark's from all the cells before setting it to the user selected cell. A good rule of thumb is to do this only if the datasource methods can't do what you need to.)
Define like this:
- (void)enumerateCellsUsingBlock:(void (^)(UITableViewCell *cell))cellBlock {
NSParameterAssert(cellBlock != nil);
for (int section = 0; section < [self numberOfSections]; section++) {
for (int row = 0; row < [self numberOfRowsInSection:section]; row++) {
NSIndexPath *cellPath = [NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell *cell = [self cellForRowAtIndexPath:cellPath];
if (cellBlock != nil) {
cellBlock(cell);
}
}
}
}
Call like this:
[self.tableView enumerateCellsUsingBlock:^(UITableViewCell *cell) {
NSLog(#"cell:%#", cell);
}];
It would be good style to typedef the block, too.

Swift 4:
for section in 0...self.tableView.numberOfSections - 1 {
for row in 0...self.tableView.numberOfRows(inSection: section) - 1 {
let cell = self.tableView.cellForRow(at: NSIndexPath(row: row, section: section) as IndexPath)
print("Section: \(section) Row: \(row)")
}
}
by steve
Iterate over all the UITableCells given a section id

Assuming a variable myTableView exists and its delegate and data source are both set:
UITableViewCell *cell;
NSIndexPath indexPath = [[NSIndexPath indexPathForRow:0 inSection:0];
for(indexPath.section = 0; indexPath.section < [myTableView numberOfSections]; ++indexPath.section)
{
for(indexPath.row = 0; indexPath.row < [myTableView numberOfRowsInSection:indexPath.section]; ++indexPath.row)
{
cell = [myTableView cellForRowAtIndexPath:indexPath];
// do something with this cell
}
}

Even simpler and more elegant:
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
// use the "cell" here
}
But obviously it doesn't fit all situations.

This how Im iterating over all table view cells even not visible ones , check my answer here :
https://stackoverflow.com/a/32626614/2715840
Hint : code in Swift.

Related

Select all rows programmatically UITableView iOS 10

I'm using a UITableView, I need to implement Select All functionality, what I have done is to select all rows programmatically by this way:
[self loopTableView:self.tableView2 aiAccesoryType:1];//Checkmark
-(void)loopTableView: (UITableView*) tableView aiAccesoryType: (int) aiAccesoryType
{
for (int i = 0; i < [tableView numberOfSections]; i++) {
for (int j = 0; j < [tableView numberOfRowsInSection:i]; j++) {
NSUInteger ints[2] = {i,j};
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:ints length:2];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//Here is your code
if(aiAccesoryType == 1)
{
//Selected
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[cell setBackgroundColor:mf_selectedColor];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
}
}
But what I need to achieve is to select all rows as iOS does when you tap a row, no matter if there is no accessory type included at selection
And also I want to get the really selected items to later set a description in each selected row

Auto Increment to each cell in different section table

I want to set a number to each cell in the table view, and i have diffent section so can't use indexPath.row.
Currently im using one variable count and increasing it in cellForRowAtIndexPath count++.
but it refresh when im scrolling table.
SO how to set auto increament number to each cell.
Try this on cellForRowAtIndexPath.. Will calculate previous sections and its row and adds with current row
NSInteger cellNumber = indexPath.row;
for (int i =0; i<indexPath.section; i++) {
cellNumber += [tableView numberOfRowsInSection:i];
}
The instance variable won't work. Your index should be calculable, as your problem states, from the indexPath (row and section), lacally within cellForRowAtIndexPath.
Try this:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"identifier"];
NSInteger count = 0;
for (NSInteger sec=0; sec < indexPath.section; sec++) {
NSInteger rows = [tableView numberOfRowsInSection:sec];
count += rows;
}
count += indexPath.row + 1;
NSLog(#"Current row is: %ld", (long)count);
return cell;
}
copy and paste the code:-
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:#"Cell"];
}
// Configure the cell...
NSInteger cellNumber = indexPath.row;
for (int i =0; i<indexPath.section; i++) {
cellNumber += [tableView numberOfRowsInSection:i];
}
cell.textLabel.text = [NSString stringWithFormat:#"%ld", (long)cellNumber];
return cell;
}
Try a combination of
NSString *string = [NSString stringWithFormat:#"%#%#",#(indexPath.section),#(indexPath.row)];
cell.tag = [string intValue];
You'll always get a unique number that you can use for reference.

cellForRowAtIndexPath: returning nil

So I've got a universal app, and I'm trying to hide a border of a cell, if the next cell in the tableView has an error state. I use this code in tableView:cellForRowAtIndexPath: to determine if the cell should hide it's bottom border or not:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DCSMCDynamicBaseCell * cell = [self.cellManager dynamicCellForComponent:[self componentForIndexPath:indexPath] atIndexPath:indexPath canShowErrors:self.shouldDisplayErrors];
NSIndexPath *nextIndexPath = [self.tableView nextRowForIndexPath:indexPath];
if (nextIndexPath) {
DCSMCDynamicBaseCell *nextCell = [self.tableView cellForRowAtIndexPath:nextIndexPath];
DCSMCDynamicBaseCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPath];
if (nextCell.invalidState) {
cell.shouldHideBottomBorder = YES;
NSLog(#"%#", currentCell);
}
}
if ([cell isKindOfClass:[DCSMCDynamicCopyCell class]]) {
[((DCSMCDynamicCopyCell *)cell).webView setNavigationController:self.navigationController];
}
[cell setDelegate:self];
self.previousIndexPath = indexPath;
return cell;
}
My nextRowForIndexPath looks like this:
- (NSIndexPath *)nextRowForIndexPath:(NSIndexPath *)indexPath {
NSInteger currentRow = indexPath.row;
NSInteger currentSection = indexPath.section;
NSInteger numberOfRows = [self numberOfRowsInSection:indexPath.section];
NSInteger numberOfSections = [self numberOfSections];
NSIndexPath *newIndexPath;
if (currentRow + 1 < numberOfRows) {
newIndexPath = [NSIndexPath indexPathForRow:currentRow + 1 inSection:currentSection];
} else if (currentSection + 1 > numberOfSections) {
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:currentSection + 1];
} else {
newIndexPath = nil;
}
return newIndexPath;
}
On my iPhone 6 simulator, everything works fine. It gets the cell and hides the bottom border of it. I may refactor this a bit in order to make sure it works in all scenarios, but for a first pass before testing, it works great.
The issue is on the iPad, every single cell returns nil when using the cellForRowAtIndexPath: method. Even weirder, on iPad, the screen is big enough so that all cells are visible at all times, so when I call reload data, the cells are already visible and should get returned by this method.
Does anyone know why a cell wouldn't get returned by cellForRowAtIndexPath:on iPad but would on iPhone?

UITableView: How to get all rows for a particular section?

What I want?
I want to add radio button functionality where a user in particular section can only select one row
How am I doing it?
I added UISwitch and based on what row user click I would like to do the following
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1 || indexPath.section == 2) {
// disable all the rows of this section except indexPath.row
}
}
Where am I stuck?
How do I get all the rows for a given section in UITableView?
P.S: I am a week old to iOS and learning, so bear with me if this is a stupid question
Use[tableView cellForRowAtIndexPath:(NSIndexPath *)]
for (int i = 0; i < [self.tableView numberOfRowsInSection:indexPath.section]; i++) {
if (i != indexPath.row) {
UITableViewCell* cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:indexPath.section]];
//Do your stuff
}
}
Swift 3:
let rowsCount = self.tableView.numberOfRows(inSection: indexPath.section)
for i in 0..<rowsCount {
let cell = self.tableView.cellForRow(at: IndexPath(row: i, section: indexPath.section)) as! UITableViewCell
// your custom code (deselecting)
}
Thanks #Soul Clinic for help with this !
This should do the trick.
- (NSArray *)tableView:(UITableView *)tableView visibleCellsInSection:(NSInteger)section
{
NSPredicate *visibleCellsInSectionPredicate = [NSPredicate predicateWithBlock:^BOOL(UITableViewCell *visibleCell, NSDictionary *bindings) {
return [tableView indexPathForCell:visibleCell].section == section;
}];
return [tableView.visibleCells filteredArrayUsingPredicate:visibleCellsInSectionPredicate];
}
You can put it in a UITableView category if you want.

How do you select uitableview rows programmatically

I am trying to achive a email like select all functionality in uitableview where on same button tap user can checkmark or remove all checkmark and additionally user can also select/deselect rows(on didSelectRowAtIndexPath). I tried to do but its not working properly, here is my code.
- (IBAction)selectAll:(id)sender
{
if(myBoolean)
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[[self.iTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] setAccessoryType:UITableViewCellAccessoryNone];
}
}
myBoolean = NO;
[_selectUnselectButton setTitle:#"Select all Friends" forState:UIControlStateNormal];
}
else
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[[self.iTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] setAccessoryType:UITableViewCellAccessoryCheckmark];
NSLog(#"%d-%d",s,r);
}
}
myBoolean = YES;
[_selectUnselectButton setTitle:#"Unselect all Friends" forState:UIControlStateNormal];
}
}
-(void)tableView:(UITableView *)tableView_ didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView_ cellForRowAtIndexPath:indexPath];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
I wrote a sample code that I adapted to your needs.
Basically it is
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *unifiedID = #"aCellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:unifiedID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:unifiedID];
}
cell.textLabel.text = [self.states objectAtIndex:indexPath.row];
//if the indexPath was found among the selected ones, set the checkmark on the cell
cell.accessoryType = ([self isRowSelectedOnTableView:tableView atIndexPath:indexPath]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *state = [self.states objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if([self isRowSelectedOnTableView:tableView atIndexPath:indexPath]){
[self.selectedCells removeObject:indexPath];
[self.selecedStates removeObject:state];
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
[self.selectedCells addObject:indexPath];
[self.selecedStates addObject:state];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
NSLog(#"%#", self.selecedStates);
}
-(BOOL)isRowSelectedOnTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath
{
return ([self.selectedCells containsObject:indexPath]) ? YES : NO;
}
- (IBAction)selectAll:(id)sender {
[self.selecedStates removeAllObjects];
[self.selectedCells removeAllObjects];
NSUInteger numberOfSections = [self.tableView numberOfSections];
for (NSUInteger s = 0; s < numberOfSections; ++s) {
NSUInteger numberOfRowsInSection = [self.tableView numberOfRowsInSection:s];
for (NSUInteger r = 0; r < numberOfRowsInSection; ++r) {
NSIndexPath *idxPath = [NSIndexPath indexPathForRow:r inSection:s];
[self.selectedCells addObject:idxPath];
[self.selecedStates addObject:self.states[idxPath.row]];
}
}
[self.tableView reloadData];
}
- (IBAction)deselectAll:(id)sender {
[self.selecedStates removeAllObjects];
[self.selectedCells removeAllObjects];
[self.tableView reloadData];
}
- (IBAction)toggleAll:(id)sender {
if ([self.states count] == [self.selecedStates count]) {
[sender setTitle:#"select all"];
[self deselectAll:sender];
} else {
[sender setTitle:#"deselect all"];
[self selectAll:sender];
}
}
in action:
You are calling
[[self.iTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] setAccessoryType:UITableViewCellAccessoryNone];
for every row in every section within the tableView. if you have many row, this is ver inefficient, as it will deal with rows not on the screen. But this is not needed. just put every selected index path into an array and tell the tableView to reload. This will reload the visible cells and due to the implementation of -tableView:cellForRowAtIndexPath: cells wfor new rows will be correctly re-conigured.
Setting the accessory view needs to happen inside the tableView:cellForRowAtIndexPath: method. When you want to change the accessories from outside, the outside method needs to change the model first to indicate that check marks must be placed in certain cells, and then call reloadData on the UITableView.
One way to store what cells are checked is an array of NSIndexSet objects - one index set per section. In the example below I show code for a single section, but you should get an idea of how to make multiple sections work.
// This variable needs to be declared in a place where your data source can get it
NSMutableIndexSet *selected;
// You need to initialize it in the designated initializer, like this:
selected = [[NSMutableIndexSet alloc] init];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if ([selected containsIndex:indexPath.row]) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
} else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
// Do the rest of your code
return cell;
}
Now in the code where you want to set rows selected or unselected you just need to call [selected addIndex:rowToSelect] or [selected removeIndex:rowToUnselect], and call your table's reloadData.
Use selectRowAtIndexPath:animated:scrollPosition: to select a row
and deselectRowAtIndexPath:animated: to deselect a row.
For more read the UITableView docs
Try this code instead of your old one
- (IBAction)selectAll:(id)sender
{
if(myBoolean)
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[self.iTable selectRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] animated:YES scrollPosition:UITableViewScrollPositionNone];
}
}
myBoolean = NO;
[_selectUnselectButton setTitle:#"Select all Friends" forState:UIControlStateNormal];
}
else
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[self.iTable selectRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] animated:YES scrollPosition:UITableViewScrollPositionNone];
NSLog(#"%d-%d",s,r);
}
}
myBoolean = YES;
[_selectUnselectButton setTitle:#"Unselect all Friends" forState:UIControlStateNormal];
}
}
-(void)tableView:(UITableView *)tableView_ didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView_ cellForRowAtIndexPath:indexPath];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
You can follow these steps to implement this,
1) You should have a mutable array to store indexpaths
2) What you can do is, when you tap Check All or Uncheck All, do add or remove all indexpaths to/from array (which you've), reload table for update changes
3) In - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath datasource method, check into array using if([array containsObject:indexPath]), if exist mark it checked or unchecked
4) In - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath delegate method, you should check for already existence of indexpath tapped into array, as you've did in 3rd step, and add or remove indexpaths as per the condition, reload table for update changes
Take another NSMutableArray as SelectedArray
in didSelectRowAtIndexPath row You can Add remove objects from SelectedArray.
You can select a UITableViewCell calling UITableViews selectRowAtIndexPath method:
[yourtable selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
animated:NO
scrollPosition:UITableViewScrollPositionTop];
Put for loop for selectedArray to putcheckmark only in selected cells or All cells.
check my accepted answer here : Need to create a select all button for UITableView and add selections to an array in iOS
Xcode 8x
Swift 3x
Select Row
let indexPath = IndexPath(row: 0, section: 0)
mytableView.selectRow(at: indexPath, animated: true, scrollPosition: .bottom)
myTableView.delegate?.tableView!(myTableView, didSelectRowAt: indexPath)
DeSelect Row
let deselectIndexPath = IndexPath(row: 7, section: 0)
myTableView.deselectRow(at: deselectIndexPath, animated: true)
myTableView.delegate?.tableView!(tblView, didDeselectRowAt: indexPath)
you can do something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self doSomethingWithRowAtIndexPath:indexPath];
}

Resources