UITableView section with maximum number of rows - iphone

I'm implementing a UITableView where the number of rows for a given section is limited to 4. For that matter, if the number of rows is less than 4, I add a row that is used as a placeholder for the next item to add (this is similar to the "Contacts" app).
I have a problem when I reach the maximum number of rows for the section. If I try to delete an object from that section, I get the following exception:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted).'
Of course, the problem is that my numberOfRowsInSection methods returns N+1 if the number of rows is less than 4 (one extra cell for the placeholder), which triggers this inconsistency exception.
Is there anyway around this?

I've run into a similar problem because I provide a placeholder row to act as a guide in getting a user started with a table interaction in one application. The reason it is happening is that the number of rows in the section has to match - as the exception is saying.
The way to handle it is simple only call deleteRowsAtIndexPaths in cases where you are changing the number of rows in the section.
So in your case you are saying I have four rows all with content (presumably stored in an array or some other data structure). I am deleting one of these rows but I am going to be placing a placeholder row there in it's place. So the number of rows in the section for display purposes is actually not changing.
What you want to do then is delete the item from your data structure so that when cellForRowAtIndex gets called it will load your placeholder cell properly but you don't actually need to try and remove the row from the tableview.
Alternatively if you want to get the highlighting interaction I'd try out what Ian is suggesting by removing and adding the rows in one transaction - I haven't tested that out in this situation though.
I have found the user experience to be pretty smooth without the additional animations, but that depends on your app.

I think that this is expected. When you tell it to delete a row, it takes that literally -- it wants that row gone. It sounds like you just want to CLEAR the row, in which case I'd call reloadRowsAtIndexPaths: instead, making sure that your dataSource knows that this row should now be empty.
Or - and this might be better - you could call an insertRowsAtIndexPath: right after the delete. This would probably let you animate stuff a little better. Just make sure that you enclose the delete and insert statements with [tableView beginUpdates] and [tableView endUpdates] statements.
Edit: just reread the question. Nevermind my first answer, but I think my second answer should still work.

Related

What is the expected semantics of the Sheet data? Should null values be returned?

I have a sheet with 18 columns and 137 rows. Some of the columns are empty. When I make the get sheets call (I'm using the C# SDK) the data returned has the following structure which seems anomalous to me ...
Columns --- 19 (the RowID is tacked on at the end of the list)
Rows --- 137 (good) but when I examine the number of cells in the rows, I get varying numbers: 18, 18, 17, 16, 18. It appears to drop the null-valued cells. Is this intentional to drop null cells? If so, why doesn't it always drop null cells? It's a little strange that the cell count doesn't reflect the column count.
The only way I can figure to work around this is to correlate using the ColumnId since there is no guarantee that there will be exactly 18 cells returned for each row.
It appears to drop the null-valued cells. Is this intentional to drop
null cells?
That is correct cells that have never been touched by the user are not sent via the API. This approach will reduce the payload and is beneficial for clients such as mobile.
If so, why doesn't it always drop null cells?
It will always drop the null cells (cells that have not been touched by the user).
However, there is a case where empty cell data is returned. This will occur when a user adds text in a cell, saves the sheet and then deletes the text. It will now appear that the cell is empty but we still store history for this cell so it is not truly a null cell or untouched cell.
The only way I can figure to work around this is to correlate using
the ColumnId since there is no guarantee that there will be exactly 18
cells returned for each row.
If you need to have the cell match up to an exact column (or column position) then we do recommend using the column id as the key.

Multiple rows insert auto insert in edit mode with UITextFields, similar to contacts phone/email

I am trying to recreate functionality of similar to adding/editing phone numbers or emails in the contact editing on the iPhone.
I always have an empty row with placeholder text available. In edit mode (push of a button on toolbar) I un-hide UITextField. In 'editing changed' callback I insert a new row into the section, again similar to iPhone contact editing view.
However, after the insert when I start modifying 2nd row (3rd row is inserted as expected) the value of the 1st row text field is cleared to the placeholder value.
I have read somewhere that I would need to store the text field value in row 1, at the time of 2nd row insertion, and on end editing (I assume) otherwise it would be lost? This seems quite strange to me and I was wondering if this is the only way? At what point would I go back to restore it? This of course keeps repeating as I insert a new row and modify the last row's value values in all the rows prior are being cleared.
Can anyone point me to an example of similar to contact editing functionality? If I would change the way rows are inserted by having the last row always be a "+ Add Value" row, would that help? I would not think so.
Also, It would be nice if I could change style of the 1st row from UITableViewCellEditingStyleNone to UITableViewCellEditingStyleDelete, but no matter what i have attempted including toggling editing NO/YES in the middle setting all visible rows to react to ignore transitions I can not get it to work, to have all but last row with UITableViewCellEditingStyleDelete. So I thought ok fine let me have the initial row not able to delete (not what I wanted, but I can live with it).
Please confirm, but I am guessing from reading farther that scrolling rows out of view would result in the same problem, so on textFieldDidEndEditing as well as possibly on each insert in my case, needs to be handled to store "new" data in the corresponding entry of the Data storage array for each row.
However, in my case without scroll I would probably need to do:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimat....
for all rows in the section being modified and possibly all visible rows. Confirmation are greatly appreciated.
Why reuse at all in your case?
If all the rows are on the screen at all times just create a new UITableViewCell for each.
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
configure content to go on it...
[cell.contentView addSubview:WHATEVER_YOU_CREATED];
[cell.contentView sizeToFit];
return cell;
Reusing cells is a way for those that have many rows to reuse the ones no longer visible to conserve resources.

Can I drag sections in tableview as I can do it with rows?

I use
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toIndexPath:(NSIndexPath *)destinationIndexPath{
}
method for dragging and dropping rows within and across sections. Can I perform drags with sections as well?
It's probably conceptually (as well as technically) simpler to step back a level to a table view where each row corresponds to the sections of the table you want to re-order. Maybe you could invoke this with a "re-order sections" button, show the section-table-view, re-order, then impose that re-ordering on the detail table view.
I think if you try to do it all inside a single table, you're going to frustrate yourself and your users. My 2 cents.
-Mike
Old question, I know, but I just implemented something that does this myself so I wanted to share the answer in case it helps others who stumble across this.
My basic approach was to add a row representing the section to the top of each section. I also added a "Add a new section" row with editing style of UITableViewCellEditingStyleInsert at the end of each section to allow creation of new sections directly within the view. So, for each section the rows were like:
Row 0 = Section row (visually distinguished by setting different
background color)
Rows 1 to x = normal data rows
Row x+1 = "Add a new section"
(I have a couple of simple methods to translate between "edit mode indexPath" and "real data indexPath".)
For moving sections, the proposed move is checked in the UITableViewDelegate Protocol method tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: to ensure that the section row has moved "far enough" (above or below another section row). The returned indexPath is clipped to either just above or just below the nearest section row (depending on if moving up or down) to give the user a visual indication of where it will be moved.
In tableView:moveRowAtIndexPath:toIndexPath: the section change is made in the data source, then a reloadData message is sent to the tableView to display the change. reloadData is used instead of moveSection:toSection: because with the latter, the data source and display would be out of sync due to the row move and would cause an exception.

Converting between unique NSIndexPath and unique NSUInteger (or int)

Is there a better way than what is proposed here to create a one-to-one mapping between an table view cell's NSIndexPath and a unique NSUInteger or int, in order to create a unique tag property value for a widget in the cell (a UIProgressView)?
The link adds methods through a UIKit category extension that converts between NSIndexPath and int, but they only work for less than a certain number of rows.
Granted, I may not run into that limit, but I'm wondering if there is a mapping that someone has come up with that can be guaranteed to work for any number of rows.
No, there really isn't a way that is guaranteed to work for any number of rows. If you have a table with 2 sections, each with 3 billion rows, then there's no way to map those 6 billion potential NSIndexPaths into the 4 billion 32-bit NSIntegers.
(if you were building a 64-bit app, the same thing is true, but the numbers would have to be another 4 billion times bigger in the example.)
They use a limit of 10,000 rows per section in the example you linked to; if you're seriously worried you might have more rows than that, you could use a bigger constant, i.e. you could use 1,000,000 as the maximum number of rows if you know you won't have more than 4,000 sections.
Note that a "real programmer" would presumably use 65536 as the constant, of course.
If I had a need to do this I would simply shift the section path to one half of a 32-bit int, and shift the row part to the other. Yes this means you can't have more than 65535 elements in a row (in breaking it out I assumed unsigned ints since row/section will never be negative) but I'll take an assumed limit far larger than I will ever need, over a magic constant any day.
Plus it's cheaper computationally.
Usually in this situation though I set up my table view controller as a delegate for each cell, and the cell can call back the controller with any changes along with some key value used to create the cell - after all when you are populating a cell you know how to reach into your data set to pull out values. Heck, you could even simply store the IndexPath into the cell and it can call back with that. The reason I would not use the IndexPath approach or the prior one involving flattening NSIndexPath into a tag is that if you ever start changing rows dynamically the values could become unaligned between the NSndexPath you created a cell with and the value for the current dataset. Much better to have some kind of key that will always get you back to the right data logically.

NSTableView problem deleting LAST row

It's been a while since I messed with my TableViewController so I'm a bit rusty...but now I'd like to set it up so that when a section has zero rows to display (array is empty) then it will "lie" to the controller and return 1 for numberOfRowsInSection. Then in the cellForRowAtIndexPath it will place a UILabel over the one row's cell that says something to the effect of "this section is empty."
Problem is when the table is in editing mode and user deletes the last row I get the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted).
It seems to be upset that after the swipe-to-delete a row when it tries to redraw the table it finds that last row is still there. Where/how should I be handling this? Certainly I'm crapping all over Apple's HIG to some extent but it's confusing to my users to have just a header over an empty section...
thanks!
You need to actually insert your "fake" row using -insertRowsAtIndexPaths:withRowAnimation:. From UITableView's point of view, there are no "real" rows or "fake" rows. There are whatever rows the datasource says there are. So you had 1 row, and then you deleted 1 row, and then UITableView asked you "how many rows are there" and you said 1, and UITableView said "1 - 1 = 1. Wait, what? Blam."
If you want to insert a row ("real" or "fake" is all the same), you need to tell UITableView you're inserting it.