I need to calculate the total number of rows in my UITableView, however I have multiple sections. Is there a property on the table view I can access? If not, how can I find this information?
Create an "Objective-c Category" of UITableView from the new file dialogue of xCode and add this method to it:
-(NSInteger)numberOfRowsInTotal{
NSInteger sections = self.numberOfSections;
NSInteger cellCount = 0;
for (NSInteger i = 0; i < sections; i++) {
cellCount += [self numberOfRowsInSection:i];
}
return cellCount;
}
Then import your category header into any class that you need to access this method in et voila, you have what you're looking for.
You can easily calculate this value by iterating through each section.
NSUInteger allRows = 0;
for(NSInteger i = 0; i < [tableview numberOfSections]; i++)
{
allRows += [tableview numberOfRowsInSection:i];
}
If you would like a property you can easily add the above code to a category for UITableView.
A UITableView doesn't know how many sections or rows it has, but your table view data source does. Iterate over the sections and add up the number of rows in each, if that's what you need to do. There may be an easier way, depending on how you're storing the data.
Related
I have a UICollectionView that I am populating with a nested array to help me create sections in my UICollectionView. The final array that I use to populate my UICollectionView looks like this with each nested array representing a different section in my view:
Array
Array
Dictionary
txt
utc
...
Dictionary
txt
utc
...
Array
Dictionary
txt
utc
...
I want the user to be able filter the collections view based on the txt (or other elements of the dictionary) and then animate the changes in the view using - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths; or - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths;.
Where I need help
I am stuck trying to create the NSArray of NSIndexSets that I need for the methods I mentioned above. It seems like indexesOfObjectsPassingTest of NSIndexSets would be involved but I can not get it to work properly and any examples I can find are typically just for a single array (How retrieve an index of an NSArray using a NSPredicate?) and I have not been able to adjust the code for my purposes.
Any code to help would be greatly appreciated. Thanks in advance.
I am able to create the NSArray by nesting for loops (which I am not that excited about doing) but it works. If you have any more efficient way to create the array please do let me know!
NSMutableArray *arrayOfIndexes = [[NSMutableArray alloc]init];
for (int i = 0; i < [finalCellData count]; i++) {
for (int ii = 0; ii < [[finalCellData objectAtIndex:i] count]; ii++) {
if (![[[[finalCellData objectAtIndex:i]objectAtIndex:ii]valueForKey:#"txt"] isEqualToString:#""]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:ii inSection:i];
[arrayOfIndexes addObject:indexPath];
}
}
}
Right now I have a NSMutableArray that holds 3 sprite objects. I need to be able to see if another sprite not in the Array shares the same position as any of the sprites in the array.
I tried doing this:
CCSprite *sect;
if (i > maxHealth) {
for (int j = 0; j < i; j++) {
sect = [tail objectAtIndex:j];
}
if (CGRectContainsPoint(sect.boundingBox, playerPos)) {
NSLog(#"On top");
return;
}
But it does't work. I think it's trying to see if it intersects all of them at once.
Your if is outside the for loop. It is only going to test one object; the last one accessed in the loop.
I have a static table view, created using a storyboard. I want to get its number of rows in code. How can I do that?
The best I know about is:
NSInteger rows = 0;
for (NSInteger i = 0; i < [myTable numberOfSections]; ++i)
{
rows += [myTable numberOfRowsInSection:i];
}
I use an NSArrayController in InterfaceBuilder to manage objects displayed in am NSTableView. When I selected one or multiple rows the following code gets called to delete the object(s) and update the selection.
NSIndexSet* selectedRowIndices = [m_tableView selectedRowIndexes];
if (!selectedRowIndices || selectedRowIndices.count < 1) {
return;
}
[self removeObjectsAtIndices:selectedRowIndices];
// -------------------------------------------------------
// SELECT ROW BELOW OR EQUAL TO THE LAST DELETED ROW.
// -------------------------------------------------------
// Retrieve the highest selection index (lowest row).
NSInteger highestSelectedRowIndex = NSNotFound;
for (NSUInteger index = selectedRowIndices.firstIndex; index < selectedRowIndices.count; ++index) {
if (index > highestSelectedRowIndex) {
highestSelectedRowIndex = index;
}
}
if (highestSelectedRowIndex != NSNotFound && highestSelectedRowIndex < [m_tableView numberOfRows]) {
// 1) Get the selected object for the highest selected index.
// TODO: Retrieve the object from m_tableView or m_arrayController somehow!?!
// 2) Update the table view selection.
//[self updateTableViewWithSelectedObjects:...];
}
However, I can not find out how I can identify the object that match with the highest index of the former selection.
Why? I would like to move the new selection to the row below the last selection.
ATTENTION: The above code contains several errors!
This is what I ended up with - thanks to Thomas for clarifications.
NSUInteger nextSelectedRowIndex = selectedRowIndices.firstIndex;
if (nextSelectedRowIndex != NSNotFound) {
if (nextSelectedRowIndex >= m_tableView.numberOfRows) {
nextSelectedRowIndex = m_tableView.numberOfRows - 1;
}
id nextSelection = [[m_arrayController arrangedObjects] objectAtIndex:nextSelectedRowIndex];
[self updateTableViewWithSelectedObjects:nextSelection]];
}
The indexes in an NSIndexSet are in order. There's no need for a loop to find the highest.
If you want to select a given row, just invoke -selectRowIndexes:byExtendingSelection: with the new selection you want to establish. For example, [m_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:highestSelectedRowIndex] byExtendingSelection:NO]. You don't need to know which object that is.
If you still want to know the object, you have to get the array controller's arrangedObjects and apply -objectAtIndex: to that.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Let's say I have 2 UITableViews next to eachother on an ipad in landscape-mode.
Now I want to move multiple items from one tableView to the other. They are allowed to be inserted on the bottom of the other tableView. Both have multiSelection activated.
Now the movement itself is no problem with normal cells. But in my program each cell has an object which contains the consolidationState of the cell. There are 4 states a cell can have: Basic, Holding, Parent, Child.
Basic = an ordinary cell.
Holding = a cell which contains multiple childs but which wont be shown in this state.
Parent = a cell which contains multiple childs and are shown directly below this cell.
Child = a cell created by the Parent cell.
The object in each cell also has some array which contains its children. The object also holds a quantityValue, which is displayed on the cell itself.
Now the movement gets tricky. Holding and Parent cells can't move at all. Basic cells can move freely. Child cells can move freely but based on how many Child cells are left in the Parent. The parent will change or be deleted all together.
If a Parent cell has more then 1 Child cell left it will stay a Parent cell.
Else the Parent has no or 1 Child cell left and is useless. It will then be deleted.
The items that are moved will always be of the same state. They will all be Basic cells.
This is how I programmed the movement:
*First I determine which of the tableViews is the sender and which is the receiver.
*Second I ask all indexPathsForSelectedRows and sort them from highest row to lowest.
*Then I build the data to be transferred. This I do by looping through the selectedRows and ask their object from the sender's listOfItems.
*When I saved all the data I need I delete all the items from the sender TableView. This is why I sorted the selectedRows so I can start at the highest indexPath.row and delete without screwing up the other indexPaths.
*When I loop through the selectedRows I check whether I found a cell with state Basic or Child.
*If its a Basic cell I do nothing and just delete the cell. (this works fine with all Basic Cells)
*If its a Child cell I go and check it's Parent cell immidiately. Since all Child cells are directly below the Parent cell and no other the the Parent's Childs are below that Parent I can safely get the path of the selected Childcell and move upwards and find it's Parent cell. When this Parent cell is found (this will always happen, no exceptions) it has to change accordingly.
*The Parent cell will either be deleted or the object inside will have its quantity and children reduced.
*After the Parent cell has changed accordingly the Child cell is deleted similarly like the Basic cells
*After the deletion of the cells the receiver tableView will build new indexPaths so the movedObjects will have a place to go.
*I then insert the objects into the listOfItems of the receiver TableView.
The code works in the following ways:
Only Basic cells are moved.
Basic cells and just 1 child for each parent is moved.
A single Basic/Child cell is moved.
The code doesn't work when:
I select more then 1 or all childs of some parent cell.
The problem happens somewhere into updating the parent cells. I'm staring blindly at the code now so maybe a fresh look will help fix things. Any help will be appreciated.
Here is the method that should do the movement:
-(void)moveSelectedItems
{
UITableView *senderTableView = //retrieves the table with the data here.
UITableView *receiverTableView = //retrieves the table which gets the data here.
NSArray *selectedRows = senderTableView.indexPathsForSelectedRows;
//sort selected rows from lowest indexPath.row to highest
selectedRows = [selectedRows sortedArrayUsingSelector:#selector(compare:)];
//build up target rows (all objects to be moved)
NSMutableArray *targetRows = [[NSMutableArray alloc] init];
for (int i = 0; i<selectedRows.count; i++)
{
NSIndexPath *path = [selectedRows objectAtIndex:i];
[targetRows addObject:[senderTableView.listOfItems objectAtIndex:path.row]];
}
//delete rows at active
for (int i = selectedRows.count-1; i >= 0; i--)
{
NSIndexPath *path = [selectedRows objectAtIndex:i];
//check what item you are deleting. act upon the status. Parent- and HoldingCells cant be selected so only check for basic and childs
MyCellObject *item = [senderTableView.listOfItems objectAtIndex:path.row];
if (item.consolidatedState == ConsolidationTypeChild)
{
for (int j = path.row; j >= 0; j--)
{
MyCellObject *consolidatedItem = [senderTableView.listOfItems objectAtIndex:j];
if (consolidatedItem.consolidatedState == ConsolidationTypeParent)
{
//copy the consolidated item but with 1 less quantity
MyCellObject *newItem = [consolidatedItem copyWithOneLessQuantity]; //creates a copy of the object with 1 less quantity.
if (newItem.quantity > 1)
{
newItem.consolidatedState = ConsolidationTypeParent;
[senderTableView.listOfItems replaceObjectAtIndex:j withObject:newItem];
}
else if (newItem.quantity == 1)
{
newItem.consolidatedState = ConsolidationTypeBasic;
[senderTableView.listOfItems removeObjectAtIndex:j];
MyCellObject *child = [senderTableView.listOfItems objectAtIndex:j+1];
child.consolidatedState = ConsolidationTypeBasic;
[senderTableView.listOfItems replaceObjectAtIndex:j+1 withObject:child];
}
else
{
[senderTableView.listOfItems removeObject:consolidatedItem];
}
[senderTableView reloadData];
}
}
}
[senderTableView.listOfItems removeObjectAtIndex:path.row];
}
[senderTableView deleteRowsAtIndexPaths:selectedRows withRowAnimation:UITableViewRowAnimationTop];
//make new indexpaths for row animation
NSMutableArray *newRows = [[NSMutableArray alloc] init];
for (int i = 0; i < targetRows.count; i++)
{
NSIndexPath *newPath = [NSIndexPath indexPathForRow:i+receiverTableView.listOfItems.count inSection:0];
[newRows addObject:newPath];
DLog(#"%i", i);
//scroll to newest items
[receiverTableView setContentOffset:CGPointMake(0, fmaxf(receiverTableView.contentSize.height - recieverTableView.frame.size.height, 0.0)) animated:YES];
}
//add rows at target
for (int i = 0; i < targetRows.count; i++)
{
MyCellObject *insertedItem = [targetRows objectAtIndex:i];
//all moved items will be brought into the standard (basic) consolidationType
insertedItem.consolidatedState = ConsolidationTypeBasic;
[receiverTableView.ListOfItems insertObject:insertedItem atIndex:receiverTableView.ListOfItems.count];
}
[receiverTableView insertRowsAtIndexPaths:newRows withRowAnimation:UITableViewRowAnimationNone];
}
If anyone has some fresh ideas of why the movement is bugging out let me know. If you feel like you need some extra information I'll be happy to add it.
Again the problem is in the movement of ChildCells and updating the ParentCells properly.
I could use some fresh looks and outsider ideas on this.
Thanks in advance.
*updated based on comments
With this code there were 2 Bugs left unsolved:
Bug1: selecting them all, then moving them leaves a Child cell
Bug2: when Parent-Child-Child are the last 3 items in the list and then you try to select them all and move them it crashes.
Again single item movement worked perfect at this time.
Just when you thought it was over a single bug remains:
When you move all but a single ChildCell from a ParentCell the ChildCell wont get changed back into the BasicCell. This is an important aspect that needs to be added since it might screw up a second movement from the table.
When you have a situation as follows:
P-C-C-C-C-P-C-C
And you select(marked with x) like this:
P-C-C-C-C-P-C-Cx
It acts like this after the movement:
P-C-C-C-C-C
While expecting:
P-C-C-C-C-B
Which will screw up the movement the next time you make it.
The Parent here has a quantity of 4. But there are 5 childcells behind it.
When you are selecting the cells like this now:
P-Cx-C-Cx-C-Cx
while expected:
P-Cx-C-Cx-C-Bx
The parent shouldn't be deleted since only 2 of its 4 children are selected. But thanks to the previous movement and the code as it is now he will think 3 of its children are removed and will return this:
C-C
While expected:
P-C-C
Replacing the item in the datasource proved to be a horrendous task. It just didn't seem to stick.
This last bug was solved now as well.
The bug was caused by miscalculating the index of the child. Since the datasource hadn't updated itself yet I needed to check which index of the child in the Parent-Child structure was still left and add it to the j instead of just the +1
This piece I added after the newItem.quantity == 1 check
//replace the one child that is left and make it basic
int x;
for (x = 1; x<consolidatedItem.quantity; x++)
{
MyCell *cell = (MyCell *)[sender cellForRowAtIndexPath:[NSIndexPath indexPathForRow:x+j inSection:0]];
if (![cell isSelected])
{
break;
}
}
MyCellItem *lastChild = [sender objectAtIndex:j+x];
MyCellItem *newChild = [lastChild copyToConsolidatedStateBasic];
[sender.listOfItems replaceObjectAtIndex:j+x withObject:newChild];
Update: There were in fact lots of problems with this code. Principally the values stored in the NSIndexPaths of the selectedRows array were becoming out of date once a parent row had been deleted from the main array.
Here's the tested and working code (after working with OP)
for (int i = selectedRows.count-1; i >= 0; i--)
{
NSIndexPath *path = [selectedRows objectAtIndex:i];
//check what item you are deleting. act upon the status. Parent- and HoldingCells cant be selected, so the only check that will be made are on child and basic cells
MyCellObject *item = [sender.listOfItems objectAtIndex:path.row];
if (item.consolidatedState == ConsolidationTypeChild)
{
for (int j = path.row; j >= 0; j--)
{
MyCellObject *consolidatedItem = [sender.listOfItems objectAtIndex:j];
if (consolidatedItem.consolidatedState == ConsolidationTypeParent)
{
//copy the consolidated item but with 1 less quantity
MyCellObject *newItem = [consolidatedItem copyWithOneLessQuantity];
if (newItem.quantity > 1)
{
newItem.consolidatedState = ConsolidationTypeParent;
[sender.listOfItems replaceObjectAtIndex:j withObject:newItem];
}
else if (newItem.quantity == 1)
{
MyCellObject *child = [sender.listOfItems objectAtIndex:j+1];
child.consolidatedState = ConsolidationTypeBasic;
[sender.listOfItems replaceObjectAtIndex:j+1 withObject:child];
}
if (newItem.quantity <= 1) {
[sender.listOfItems removeObjectAtIndex:j];
// Update indexPath row values for selected items that are above the removed row
for (int k = i; k >= 0; k--)
{
NSIndexPath *aPath = [selectedRows objectAtIndex:k];
if (aPath.row >= j)
{
[selectedRows replaceObjectAtIndex:k withObject:[NSIndexPath indexPathForRow:(aPath.row -1) inSection:0]];
}
else
{
break;
}
}
path = [NSIndexPath indexPathForRow:(path.row -1) inSection:0]
}
}
}
}
[sender.listOfItems removeObjectAtIndex:path.row];