On the WWDC 11 video 'session 125 - UITableCiew Changes, Tips, Tricks' at 24:30+ Mr Luke Hiesterman is giving a demo which adds a cell to the table view when a cell is selected.
I want to add that functionality to my IOS application, but I can't figure out how to make it.
Some code is not shown in the demo-video. And there is no downloadable source of the demo.
Can anyone help me out?
EDIT:
I can add a new row below the selected row but it is an other custom cell.
(I have a list of contracts which you can accept)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return ((indexPath.row >= [_contracts count]) ? 40 : 60);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_contracts count] + ((_addedRow) ? 1 : 0);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseID = #"contract_cell";
ContractTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID];
if(!cell)
{
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"ContractTableViewCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
[cell setBackgroundView:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"contract_cell.png"]]];
}
NSLog(#"row?: %d", indexPath.row);
//I thought this would work... not.
if((indexPath.row >= [_contracts count]))
{
[cell.label setText:#"new"];
}
else
{
Contract *contract = [_contracts objectAtIndex:indexPath.row];
[cell.label setText:#"test"];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(!_addedRow)
{
_addedRow = YES;
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
return [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
}
For example I press the first row. A row is added below the first one. But now the cellForRowAtIndexPath.. is called for row 2 (needs to be the new custom)
how can I check if it is a new one?
You need to remember which of your rows is selected to show the added row at the correct index. Currently you're always assuming that the added row is the very last one because you're configuring it in tableView:cellForRowAtIndexPath: only if the index is bigger than your number of contracts.
Say you have five contracts and the third one is selected. if((indexPath.row >= [_contracts count])) is only true for the last row but actually want this condition to be true for the fourth row, so it should be if (indexPath.row == selectedRowIndex + 1) (you need to store the selected row index in some instance variable).
This answer should help you out. It tells you how to put your UITableViewController into "update mode" and then lets you insert new rows (even with animation): UITextField in UITableViewCell - adding new cells
Related
I am working with grouped tableview with multiple sections.
and I have to implement the functionality of multiple selection on didselectrow at indexpath
method. my code is as follows.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
which allows me to select multiple cells
but when I scroll my tableview at that time my selection disappears.
Your selection goes off when you scroll because it calls cellForRowAtIndexPath and there you have not handle selection.
To avoid this problem you can do as follows:
In didSelectRowAtIndexPath you can save index path of selected row as follows:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
//remove index path
[selectedIndexPathArray removeObject:path];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[selectedIndexPathArray addObject:path];
}
}
and in cellForRowAtIndexPath you can check whether cell is selected or not.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//If selectedIndexPathArray contains current index path then display checkmark.
if([selectedIndexPathArray containsObject:indexPath])
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Try this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
if (self.tableView.isEditing) {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
} else {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return cell;
}
-(UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleMultiSelect;
}
-(IBAction) switchEditing {
[self.tableView setEditing:![self.tableView isEditing]];
[self.tableView reloadData]; // force reload to reset selection style
}
Hope this helps solving your problem.(ref)
Your selection disapear cause the method CellForRowAtIndexPath will be called at scroll.
You need to set the accessory again.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
// here
...
}
You are getting this issue because you are not tracking your row selections.When the callback method cellForRowAtIndexPath gets called for the rows that had disappeared/(scrolled up/down) the cell object no longer remembers whether it was selected or not. (reason those selections are disappearing)
I would suggest you to use a collection like NSMutableArray/NSArray to track the selected rows.
You can use either of these approaches.
This would be a quick working fix:
Add/Remove the index path object in didSelectRowAtIndexPath based on the users selection
and then based on the contents of that array u can toggle the value of cell.accessoryType for the corresponding cell.
Ideally,you can use a data bean/model with some boolean member called selected and u can update its value based on the selection made.Then instead of simply adding those index path u can add those meaningful data bean objects onto your array and get the selections back from the selected property of the bean.This approach would help u in getting back the row selections even if the user kills and restarts the app provided u persist the bean objects in database/archive..(But it all depends on ur use case and requirements!)
Hope this helped!
I want to add row dynamically. I have tableview list of building names. If some one choose building(didSelectRowAtIndexPath) then respective floors of building should get added dynamically as subrow. Its like maximizing and minimizing the subrow on respective building list selection. How do I do this. Thanks in advance...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// There is only one section.
if (tableView == indoortable || tableView == indoortable_iPad)
{
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of time zone names.
if (tableView == indoortable || tableView == indoortable_iPad)
{
return [indoorZones count];
}
}
cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == indoortable || tableView == indoortable_iPad)
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleGray; //cell bg
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Set up the cell.
//cell.textLabel.text = [copyListOfItems objectAtIndex:indexPath.row];
cell.textLabel.text =[indoorZones objectAtIndex: indexPath.row];
//[cell setIndentationLevel:[[self.indoorZones objectAtIndex:indexPath.row] intValue]];
return cell;
}
}
didSlectRowAtIndexPath method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
zonesFloor = [[NSMutableArray alloc]init];
zonesFloorA = [[NSArray alloc] initWithObjects:#"Gr fl",#"1st fl",#"2nd fl",nil];
[zonesFloor addObject:zonesFloorA];
if (tableView == indoortable )
{
NSUInteger i=indexPath.row+1;
for (NSArray *count in self.indoorZones) //app is crashing here giving error.......Collection <__NSArrayM: 0x4b1d550> was mutated while being enumerated.
{
[zonesFloor addObject:[NSIndexPath indexPathForRow:i inSection:0]];
[self.indoorZones insertObject:zonesFloor atIndex:i++];
}
[[self indoortable] beginUpdates];
[[self indoortable] insertRowsAtIndexPaths:(NSArray *)zonesFloor withRowAnimation:UITableViewRowAnimationNone];
[[self indoortable] endUpdates];
}
if (tableView == indoortable_iPad )
{
//some logic
}
[tableView deselectRowAtIndexPath:selectedIndexPath animated:NO];
}
It Gives following error [__NSArrayI compare:]: Or [NSIndexPath _fastCStringContents:]: unrecognized selector sent to instance. I tried many ways but may be I am lacking somewhere. Please suggest. thanks in advance.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSIndexPath *selectedIndexPath = [self.indoortable indexPathForSelectedRow];
zonesFloorA = [[NSArray alloc] initWithObjects:#"Gr fl",#"1st fl",#"2nd fl",nil];
if (tableView == indoortable )
{
for (NSString *str in zonesFloorA) {
[indoorZones addObject:str];
}
//[[self indoortable] beginUpdates];
//[[self indoortable] insertRowsAtIndexPaths:(NSArray *)zonesFloor withRowAnimation:UITableViewRowAnimationNone];
//[[self indoortable] endUpdates];
}
if (tableView == indoortable_iPad )
{
//some logic
}
[tableView reloadData];
}
may this meet your requirement
Okay, so not to sound mean, but there are almost too many issues here to count.
Let's start with a basic explanation of how tableView's work so that you can start to fix this:
First, the tableView asks how many sections are in the table by calling:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
}
In your code, you simply tell it that it has one section. However, in your later code, when you try to add rows to your table, you tell it that you want to add your rows to a second section (with an index of 1). Therefore, you either need to add these rows to section 0 instead, or update the above method to tell it that, sometimes, there are two sections.
Second, the tableView asks how many rows are in each section of the table by calling:
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
}
In your code, you are simply returning the number of zones. However, like above, you need to include the rows that you have added to your table. If you are adding them to a different section, then you need to return different values, depending on how many rows are in the section with the index asked for in the section variable. If they are all in the same section, then you need to add them up and return the correct value.
Third, the tableView asks for an actual cell for the row by calling:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
}
In your code, you are only returning a cell which has data populated by the indoorZones array, but you also need to supply cells which are configured properly for the specific zone/floor. Again, you either need to determine this by section number or row number as appropriate.
Finally, when you click on a row, the tableview tells you by calling the following method:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
In this method, you need to update your data source that is used by the previous functions so that when they get called again, they will provide the correct data. Your data source must mirror the way that you want your table view to look. In your case, you have an array called indoorZones. This is good if you just want to display a list of zones, which is what you start off doing. However, when you want to add more rows, you need to add more rows to your data source first, so that when the tableView starts this process over, it is already there.
If you want everything to stay in one section, then I would come up with a data source that can include both types of rows, and be able to distinguish between them so that cellForRowAtIndexPath can create the proper type of cell and return it.
If you want two sections, then I would add a second array for the second section (since it is not the same type of data) and return the appropriate values in each of these methods, based on which array you need to use for that section.
I hope this helps!
I made a UITableViewCell and I have 20 rows in that table in which 5 at a time on screen.
I have set accessory view checkd mark in didSelect delegate method on which row is being selected. My concern is let suppose first row is selected and its accessory type is checked, now if i scroll the table we see the sixth one is also checked. I know that the cell is reusing itself and not creating itself again.
The model should be able to handle which cell is checked and which is not. To simplify the problem, you can keep an array which will the NSIndexPaths that should be checked. If only one can be checked at the time, an ivar of the type NSIndexPath is more than enough.
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[aTableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark)
{
// Ok this one is selected, so we will remove it from the Reference Array.
}
else
{
// Ok this one doesn't has a checkMark
// First add the checkmark
[[aTableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark];
// Add the NSIndexPath to the Array of references
}
}
In the delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellId = [NSString stringWithFormat:#"cell%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId ];
if (cell==nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:cellId ] autorelease];
}
}
Set different cell ID to different cell row.
i am using below code for auto scrolling UITableview
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [DataArray count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
return 60;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[[CustomTableCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
if(isAutoScrollEnabled)
[DataTable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
cell.Time.text = [[DataArray objectAtIndex:indexPath.row]objectForKey:#"CurrentTime"];
cell.SerialNo.text = [NSString stringWithFormat:#"%d",indexPath.row+1];
return cell;
}
But it fluctates my tableview every time i reload data in tableview. Is there any solution ?
Can anyone help me ?
Thanks in advance......
What do you want?
smooth scrolling to down?
or
scrolling to new cell position?
first case(smooth scrolling) should use NSTimer.
second case(to new cell position) should override UITableView[updateData]
I'm not sure exactly what you are saying. But I will say that calling scrollToRowAtIndexPath in your cellForRowAtIndexPath looks like it could cause problems. What you are doing is telling it to scroll each time it generates a cell. That doesn't look right.
I also have to wonder if you really want to be animating that scrolling.
For the iPhone, is it possible to configure a UITableView such that it will allow multiple-selection?
I've tried overriding -setSelected:animated: for each UITableViewCell, but trying to fudge the required behavior is tricky as it's difficult to separate the real unselections from the ones where the UITableView thinks I've unselected due to selection of another cell!
Hope someone can help!
Thanks,
Nick.
Following property should work fine if you are developing app for iOS5.0+
self.tableView.allowsMultipleSelection = YES;
The best way to do this would be to a checkmark per selected row.
You can do that by setting the accessoryType on the selected UITableViewCell instances to UITableViewCelAccessoryCheckmark.
To deselect the row, set it back to UITableViewCellAccessoryNone.
To enumerate which cells/rows were selected (say, upon clicking a button), simply iterate over the cells of the table looking for UITableViewCellAccessoryCheckmark. Or, manage some NSSet or the like in your table view delegate in the "did select" delegate methods.
Use the following code to set up the cell accesory types:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *thisCell = [tableView cellForRowAtIndexPath:indexPath];
if (thisCell.accessoryType == UITableViewCellAccessoryNone) {
thisCell.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
thisCell.accessoryType = UITableViewCellAccessoryNone;
}
}
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
//add your own code to set the cell accesory type.
return UITableViewCellAccessoryNone;
}
Jeff Lamarche has a tutorial on how to do this here:
http://iphonedevelopment.blogspot.com/2008/10/table-view-multi-row-edit-mode.html
I've not tried the code but it's been on the back of my mind for a while, knowing the day will come when I need it.
I backported allowsMultipleSelectionDuringEditing and allowsMultipleSelection from iOS5 to older iOS. You can fork it at https://github.com/ud7/UDTableView-allowsMultipleSelection
It's drop in replacement and only thing you need to do is change UITableView to UDTableView (in code or interface builder)
From the HIG:
Table views provide feedback when users select list items. Specifically, when an item can be selected, the
row containing the item highlights briefly when a user selects it to show that the selection has been received.
Then, an immediate action occurs: Either a new view is revealed or the row displays a checkmark to indicate
that the item has been selected. The row never remains highlighted, because table views do not display a
persistent selected state.
You'll need to roll your own multiple selection style, either with something like Mail, or using the checkmark accessory on your cells.
Guys for multiple selection you just need
self.tableView.allowsMultipleSelection = YES;
on viewDidLoad and
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
tableViewCell.accessoryView.hidden = NO;
// if you don't use custom image tableViewCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
tableViewCell.accessoryView.hidden = YES;
// if you don't use custom image tableViewCell.accessoryType = UITableViewCellAccessoryNone;
}
I was searching for the same issue and the answer of Bhavin Chitroda sovled it for me but with some addition to keep the check mark as it was while scrolling.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ( [array indexOfObject:indexPath] == NSNotFound ) {
[array addObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
[array removeObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
The addition:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
// Your code here
.
.
.
if ( [array indexOfObject:indexPath] == NSNotFound ) {
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
return cell;
}
If you're trying to do something like Mail's multiple-select (to delete mail, for example), then you're probably going to have to manage all the selection yourself. Multiple row selection isn't something that's standard on the iPhone. Mail solves this by using checkmarks to indicate which rows have been selected.
blue highlighted row as an indicator of whether a row is selected is actually discouraged according to the HIG page 121. Checkmarks will do the trick.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
int selectedRow = indexPath.row;
cout << "selected Row: " << selectedRow << endl;
UITableViewCell *indexPathForCell = [tableView cellForRowAtIndexPath:indexPath];
if (indexPathForCell.accessoryType == UITableViewCellAccessoryNone) {
indexPathForCell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
indexPathForCell.accessoryType = UITableViewCellAccessoryNone;
}
}
then add your arraying or how ever you wish to store the data of which were selected.
Note: This does not work in iOS 4+. This is a private, undocumented constant. Do not use it.
If you are not planning to submit your app to the App Store, you can invoke multi-row edit mode by implementing the following method in your UITableViewController delegate:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return 3; // Undocumented constant
}
Tested with iOS4.3 - 6.0
-(void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
if ([controller.searchResultsTableView respondsToSelector:#selector(allowsMultipleSelectionDuringEditing)]) {
controller.searchResultsTableView.allowsMultipleSelectionDuringEditing = YES;
}
else {
controller.searchResultsTableView.allowsSelectionDuringEditing = YES;
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellAccessoryCheckmark;
}