RealTime: Adding rows to the table in UITableViewController - iphone

I just checked the datasource and the UITableView of the app and they seem to be working well.
I can add a row to the TableView by using the code - [...initWithObjects:(id),nil] and I could do this at code level.
This is what I want to do.
Create an Add button or a '+' sign on top of the table.
So while the App is running.. If I click that, I should be able to set a name for that row.
If I need to create more rows and set names for them, I just press the add button and I again type new names for the rows .
How do I go about this in real time?

The UITableView method -insertRowsAtIndexPaths:withRowAnimation: is used to add rows to a table programmatically.
This answer addresses your problem fairly directly.

It's fairly straightforward... and this is from memory, so I might have a typo here or there. The idea should work though.
In tableView:numberOfRowsInSection: you give return [myArray count] + 1; (you're adding one row to the total)
In tableView:cellForRowAtIndexPath: the cell where [indexPath row] == [myArray count] is where make a cell with "Add Row" text, rather than whatever your data source is, because it goes to [myArray count]-1. (I think that makes sense).
for example,
if([indexPath row] == [myArray count]){
cell.textLabel.text = #"Add New Item";
} else {
cell.textLabel.text = [[myArray objectAtIndex:[indexPath row]]
valueForKey:#"title"];
}
return cell;
Then you would insert the new record into your myArray and the table.
As for actually changing the new row details, you have some options of creating a custom cell with some kind of textual input or using a modal view controller with a prompt. It's really up to you.

Related

Populating UITableView row 0 static and the rest with NSArray

In one section of my app I have a UITableView which is working fine right now. I would like to set row 0 cell.textLabel.text to #"Some string". Once row 0 has been set I would then like to load the rest of the rows from an array. Currently on load my array populates the table view but I'm trying to set row 0 as a sticky. The closest example I can think of is a forum topic that is set to stay at the top. My array is constructed of returned data from a web service call.
It's been a while since I've messed with table views, and I'm having a blank on this one.
The table view is 1 section, and I get the rows by counting the elements in the array. Since I would like to create an additional cell (row 0) I would call [array count] + 1. I don't know if this approach is the best one which is why I'm reaching out to the community here.
Any insight or a shove in the right direction would be great at this point.
You're on the right track:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count]+1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if ([indexPath row] == 0) {
// Code for first
[[cell textLabel] setText:#"First cell"];
} else {
[[cell textLabel] setText:[array objectAtIndex:[indexPath row]-1]];
}
return cell;
}
If you want the top of your table to be "sticky", why not consider using that string as a section header or title? In this case, the header stays visible at all times until the next section (e.g. if you had two sections, that is) is fully on the screen.
In any event, in one of my current projects I'm required to do roughly the same thing that you're doing and I have a static string being returned in row 0 (which scrolls off the top of screen when the table view scrolls down).
And in my UITableViewDataSource method, I always add one for the static cell to the number of objects in my array and in my "cellForRowAtIndexPath:" method, I increment the row by one when the indexPath.row is not zero. And if it is zero, I return my static string.
And dark_knight provides some nice sample code that illustrates what I was describing to you. So +1 to him/her.

TableViewCell's textLabel value returns to 1 when cell is scrolled out of the view

i have a table view in which i can add 1 or subtract 1 to the value of my cell.textLabel.text but when i switch views and return or scroll a cell out of the view, and when it comes back into view, the textLabel's value returns to 1 which is the original starting point! Please help! Here is the code to add and subtract 1:
- (IBAction)addLabelText:(id)sender{
cell = (UITableViewCell*)[sender superview]; // <-- ADD THIS LINE
cell.textLabel.text = [NSString stringWithFormat:#"%d",[cell.textLabel.text
intValue] +1];
}
- (IBAction)subtractLabelText:(id)sender
{
cell = (UITableViewCell *)[sender superview];
if ( [[cell.textLabel text] intValue] == 0){
cell.textLabel.text = [NSString stringWithFormat:#"%d",[cell.textLabel.text intValue] +0];
}
else{
cell.textLabel.text = [NSString stringWithFormat:#"%d",[cell.textLabel.text intValue] -1];
}
}
This is happening because, the cells will be re-used on scrolling. The table view's datasource method will be invoked, hence the values get reset to the original value. You can maintain an array of NSNumbers as a datasource to the tableview (is, in cellForRowAtIndexpath: , set the text fo the cell label from the array). Each time you need to add or subtract, do it the corresponding NSNumber obj and re-load the tableview.
Seems like you are allocating a new cell each time.. and not using the cell re-usablility method.
In your case, when you are performing arithmetic actions to your previous values and you don't have an array to store previous values. The easiest way to fix this is make your Cell-Identifier unique. (something like #"Cell-%d",indexPAth.row)
Note: However, more efficient way would be to save your result in the array you are populating your data from, without making you Cell-Identifier unique.
You are not updating your data modal. That is why it is taking the original content value.
After change the cell text value reload the tableview [self.tableview reloadData]

Change the number of rows in a section depending on a variable

In my 'Sectioned' UITableView I have two sections, the first one for Attributes like name and the second one is editable where you can add objects.
Here's a picture of it:
alt text http://fwdr.org/bm9w
There's an attribute that the user can change (in this case Type) which would to change the number of rows in second section. To be more specific, if one property is selected the maximum number of rows is 2 and then there would be no Add New… row.
Here's what I've tried (in my UITableViewController) …
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([self isToManyRelationshipSection:section]) {
NSArray *sectionKeys = [rowKeys objectAtIndex:section];
NSString *row0Key = [sectionKeys objectAtIndex:0];
if ([[NSString stringWithFormat:#"%#", [managedObject valueForKey:#"type"]]
isEqualToString:[NSString stringWithFormat:#"One on One"]] && [[managedObject valueForKey:row0Key] count] == 2){
return [[managedObject valueForKey:row0Key] count];
}
return [[managedObject valueForKey:row0Key] count] +1;
}
return [rowLabels countOfNestedArray:section];
}
But if the user tries to remove a row when there are already 2 rows the app crashes because there would be the same number of rows before and after the deletion.
How do I get around this and do this properly?
First off, the numberOfRows.. method wouldn't appear to me as the proper place to do this kind of logic. I'd rather create a seperate method to determine and return the current game mode (and keep the gamemode updated whenever the user selects another mode). I'd associate integers with gamemodes (1 = "one on one", 2 = "all against all"...), that way in the numberOfRows Method you just need to check "is the current section == 1?" && "which game mode are we in?" to determine how many rows are needed.
If you keep track of the gamemode properly, you don't need to check for equal strings anymore, too.
I ended up doing this differently to what I first wanted to do.
My reasoning was that my current implementation gave me an error in the debugger which in turn crashed the app. This error seemed un-avoidable so I decided to simply disable the cell if there are two rows, instead of hiding it.
To do this in my tableView:cellForRowAtIndexPath: method I added the following code (in my if statement) to make it look un-selectable to the user:
cell.textLabel.textColor = [UIColor lightGrayColor];
cell.editingAccessoryType = UITableViewCellAccessoryNone;
And in the tableView:didSelectRowAtIndexPath: method I left my if statement blank so it wouldn't push another view controller or add a object. Although I did include this code to deselect the row:
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];

Referencing UISwitches within UITableCells

I have a table view which I'm using for some settings in my app. The tables cells are all default (no customisation at all), and simply contain some text for their label and a UISwitch for the accessory view.
My problem is that I need a reference to the switch so that I know when it has been switched on and off.
Currently I am setting the 'tag' property of the switch to be that of the cell's index within the table (grabbed from [indexPath row] in tableView:cellForRowAtIndexpath:).
This is fine when you only have one Section in your table, but I am now adding a new section. The problem is that they are both 0 based indexed so the switches in each section will end up reusing the tags - which isn't good.
Any suggestions on a better way to achieve this?
Thanks.
If you know roughly how many sections and rows you will have, like oh, say, not more than 1 million rows per section, just hash the section and row like this:
const int oneMillion = 1000000;
int tag = (section * oneMillion) + row;
slider.tag = tag;
Then, to figure out the section and row, reverse the logic:
int tag = slider.tag;
int row = tag % oneMillion;
int section = tag / oneMillion;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row inSection: section];
Now get the slider that is in the cell in that section,row of the table
UITableViewCell *sliderCell = [tableView cellForRowAtIndexPath: indexPath];
UISlider *slider = [[sliderCell.contentView subviews] objectAtIndex: 0];
This assumes the slider is always the only view in the contents of the cell.
This method is a bit longer than some of the other suggestions above, but it keeps you from having to cache references off to the side.
For each cell, set a delegate link back to the table view controller, and also some kind of row reference ID - then wire the switch to a cell IBAction method, that calls back to the delegate with the reference ID for that cell.
What you can do is either have an Array of arrays or a dictionary, key it by the section number (or in case of the array they will be in order of the section numbers), now to retreive a switch all you do assuming you know the section and the row number
UISwitch *switch=[[switchArray objectAtIndex:section] objectAtIndex:row];
or if you have a dictionary
UISwitch *switch=[[switchDictionary objectForKey:section] objectAtIndex:row];

iPhone SDK: Inserting and updating a UITableView with a new row

I have a tableView that needs to be updated after information has been inserted from another view. If I perform a
[self.tableView reloadData];
The very next time I insert more information in another view and try to reload the table, all the currently visible rows are duplicated.
In other words, when I start up the app I have:
tableView:
Row 1
Row 2
Then I submit some information that will also show up in the table and suddenly I have:
tableView
Row 1
Row 2
Row 3 <- info I just added
Row 1
Row 2
My numberOfRowsInSection implementation looks like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [ItemsController sharedItemsController].count;
}
My cellForRowAtIndexPath implementation looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ItemsController* controller = [ItemsController sharedItemsController];
NSMutableArray* recentItems = controller.listOfRecentItems;
CustomCell *cell = nil;
NSUInteger row = [indexPath row];
if( row < recentItems.count )
{
Items* item = [recentItems objectAtIndex:row];
if( recentCellData == nil )
recentCellData = [[NSMutableDictionary alloc] initWithCapacity:[indexPath length]];
if( [recentCellData count] > 0 )
cell = [recentCellData objectForKey:[NSString stringWithFormat:#"%d", row]];
if (cell == nil) {
UIViewController * view1 = [[UIViewController alloc] initWithNibName:#"CustomCell" bundle:nil];
cell = (CustomCell*)[view1 view];
[recentCellData setObject:cell forKey:[NSString stringWithFormat:#"%d",row]];
}
// do some other stuff here
}
// Set up the cell
return cell;
}
What's the best way to update the table and avoid duplicating the currently visible rows.
Thank in advance for all the help!
The error isn't in how you're reloading the table, it's in how you're providing data to it. Set a breakpoint in the data source methods and the method that adds new rows to see where you're going wrong.
You'll only end up with five items if tableView:numberOfRowsinSection: returns 5. Thats the simple answer to your question, but I see other problems here. I'm wondering why you have this test: row < recentItems.count. Is that array the same thing as [ItemsController sharedItemsController].count? You really need to be using the same array for both methods.
(Also, it's not a syntax error, but you shouldn't use the property syntax for things that aren't declared as properties. You should write [recentItems count] instead.)
I'm also confused by the code you use to set up the cell. Cells are meant to be reusable. That is, you create one cell, then reconfigure it every time in your implementation of tableView:cellForRowAtIndexPath:. Your code creates a cell for each item in your list. This is very memory-inefficient, and will likely crash your program due to insufficient memory on the iPhone if you keep lots of cells in memory like this.
The recommended approach is to call dequeueReusableCellWithIdentifier:. If that returns nil, then you set up a cell using the initWithFrame:reuseIdentifier: initializer. The table view is very smart, and will only ask you to redraw the cell when it needs you to.
Your recentCellData dictionary looks really shaky to me, too. What if you insert an item after the item with key #"2"? All the items with key #"3" onward will need to be shifted one element to the right to work the way you expect. That's a ton of bookkeeping that seems rather unnecessary to me. If you really needed something like this -- and to be clear, I don't think you do -- why wouldn't you use an NSMutableArray, which is much easier to use?
I added a bit more info above.