I'm working on a prototype (I'm way too green on Objective C) which has a tableview with two different custom prototype cells, each with their own set of components, layout and height. The expected behavior is that when selected, the row expands and shows cell type b, and the previously selected row returns to the former height and cell type.
Ok, so the issue I'm having is that I can't get the previously selected row to change it's cell type, the height is working ok so I end up displaying a shorter version of the expanded cell but not having the components and layout of the desired cell type, and if I scroll through the tableview and return to the location where the previously selected row is it displays the correct cell type, as if it hasn't refreshed the rest of the rows.
Here's my code for tableView cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier;
UITableViewCell *cell = nil;
if(indexPath.row == self.selectedIndex){
CellIdentifier = #"ExpandedCell";
CollapsedCell *collapsedCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell = collapsedCell;
}else{
CellIdentifier = #"LocationCell";
LocationCell *locationCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
locationCell.transparentBorder.layer.borderWidth = 5.0f;
locationCell.transparentBorder.layer.borderColor = [UIColor colorWithRed:255/255 green:255/255 blue:255/255 alpha:0.7].CGColor;
cell = locationCell;
}
BHPlace *currentPlace = self.data[indexPath.row];
NSString *name = currentPlace.name;
LocationCell *locationCell = (LocationCell*)cell;
locationCell.nameLabel.text = name;
cell.backgroundColor = [UIColor clearColor];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
and here's the code for my didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:self.selectedIndex inSection:0] animated:YES];
[self.tableView beginUpdates];
self.selectedIndex = (int)indexPath.row;
NSArray *paths = #[indexPath];
[self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self goToMarker:self.data[indexPath.row]];
}
As usual I'm sure it's a simple issue and there's something I'm not getting right but can't find the right approach,
Thank you all for your time
You just need to reload both the previously selected and the newly selected rows -
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSMutableArray *paths=[NSMutableArray new];
if (self.selectedIndexPath != nil) {
[paths addObject:self.selectedIndexPath];
[tableView deselectRowAtIndexPath:self.selectedIndexPath animated:YES];
}
[self.tableView beginUpdates];
self.selectedIndexPath = indexPath;
[paths addObject:indexPath];
[self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self goToMarker:self.data[indexPath.row]];
}
Note that I changed your int property to an NSIndexPath * self.selectedIndexPath
Related
I'm currently using the below code for didSelectRowAtIndexPath. With the way I have my code written now, if a user taps a cell a green checkmark appears. If they tap the same cell again, the green checkmark disappears. That said, I want to make it so that if the user taps a different cell after making a selection, the green checkmark should disappear from the previously selected cell. How can I accomplish this?
ViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if(_selectedRowIndex && indexPath.row == _selectedRowIndex.row) {
ClientTableViewCell *cell2 = [tableView cellForRowAtIndexPath:indexPath];
cell2.greenCheck.image = [UIImage imageNamed:#""];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
_selectedRowIndex = nil;
}
else { self.selectedRowIndex = indexPath;
ClientTableViewCell *cell2 = [tableView cellForRowAtIndexPath:indexPath];
NSDictionary *client = self.sectionClients[indexPath.section][indexPath.row];
cell2.greenCheck.image = [UIImage imageNamed:#"added.png"];
NSLog(#"SELECTED");
}
[tableView beginUpdates];
[tableView endUpdates];
}
You can store the last selected indexpath, when a new cell is selected, you get the previous cell with the stored indexpath and deselect it.
#implementation ViewController
NSIndexPath *lastIndexPath;
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if (lastIndexPath != nil){
UITableViewCell *oldCell = [self.tableView cellForRowAtIndexPath:lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
if(lastIndexPath != indexPath) {
lastIndexPath = indexPath;
UITableViewCell *cell2 = [tableView cellForRowAtIndexPath:indexPath];
cell2.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
lastIndexPath = nil;
}
[tableView beginUpdates];
[tableView endUpdates];
}
I have a simple tableView in which i change the cell whenever i click it in the function like this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self shuftleCellExpansion:indexPath.row];
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}
Second Functions:
-(BOOL) shuftleCellExpansion:(int) indexP
{
if([opendedCells containsIndex:indexP])
{
[opendedCells removeIndex:indexP];
return YES;
}
else
{
[opendedCells addIndex:indexP];
return YES;
}
}
Cell for index Function:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([self isCellInExpandedList:indexPath.row])
{
NSLog(#"Cell is in Expanded State %hhd for Index Path %ld",[self isCellInExpandedList:indexPath.row], (long)indexPath.row);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"poCell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"poCell"];
cell.backgroundColor = [UIColor blueColor];
}
[[cell textLabel] setText:[searchResults objectAtIndex:indexPath.row]];
[cell imageView].image = [UIImage imageNamed:#"someicon.png"];
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"poCell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"poCell"];
cell.backgroundColor = [UIColor redColor];
}
[[cell textLabel] setText:[searchResults objectAtIndex:indexPath.row]];
return cell;
}
No the issue is that when ever i change a specific cell and then scroll it the cell at after the specific cells (i.e. after total visible cells) i see the same changed cell .
What is the efficient way to get Rid of this?
Every cell which go off the screen goes to reuse pool (mostly all data are erase) and if you scroll back the cell is populated in cellForRowAtIndexPath: method again.
You need to change the data source if you want to keep the changes.
In your didSelectRowAtIndexPath: you need to also make the changes in the array (I assume you use array to read the data from in cellForRowAtIndexPath:) you use in cellForRowAtIndexPath: to populate cell.
Hope this help.
// EXTENDED
To make it work you need to amend shuftleCellExpansion: method. In opendedCells you swap the indexes, I believe it reflect the changes in table view. In the same method you have to reflect the changes for searchResults array as well (swap the object as you did for opendedCells).
I have a UIViewController with a UITableView which has multiple accessory checkmark implemented into it. My problem is, when I click some cells in the tableview it gets checked but there will be some other cell also checked below. I can view it when I scroll down the tableview. I would only want the cell to be checked whichever the user is clicking and not the extra cells. Please let me know how can I do it. Thanks.
Here is the code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [someData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text=[self.someData objectAtIndex:indexPath.row];
cell.textLabel.font=[UIFont fontWithName:#"Times New Roman" size:11];
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
[cell.textLabel setNumberOfLines:2];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
if ([selectedCell accessoryType] == UITableViewCellAccessoryNone) {
[selectedCell setAccessoryType:UITableViewCellAccessoryCheckmark];
NSArray *arrayOfIndexPathsTableOne = [self.tableView indexPathsForSelectedRows];
for (int i = 0; i < [arrayOfIndexPathsTableOne count]; i++) {
NSIndexPath *indexPathImInterestedIn = [arrayOfIndexPathsTableOne objectAtIndex:i];
UITableViewCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPathImInterestedIn];
[saveData addObject:[NSString stringWithFormat:#"%#", currentCell.textLabel.text]];
}
} else {
[selectedCell setAccessoryType:UITableViewCellAccessoryNone];
NSArray *arrayOfIndexPathsTableOne = [self.tableView indexPathsForSelectedRows];
for(int i = 0; i < [arrayOfIndexPathsTableOne count]; i++) {
NSIndexPath *indexPathImInterestedIn = [arrayOfIndexPathsTableOne objectAtIndex:i];
UITableViewCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPathImInterestedIn];
[saveData removeObject:[NSString stringWithFormat:#"%#", currentCell.textLabel.text]];
}
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
This is a cell reuse problem.
To fix, ensure that each time you return a cell from cellForRowAtIndexPath: you always set the selection (checkmark) status. That means explicitly setting it true and false as appropriate.
There are only 2 things you need:
Disable multiple selection on your UITableView.
Implement setSelected:(BOOL)selected in your UITableViewCells and make it select/deselect the cell's sub-views (your checkmarks) accordingly.
When you select table you show check box on it but when you scroll and the cell desapeare and it goes to reusable pool after you scroll back the cell is taken from this pool and it doesn't remember the 'state'.
The solution is create NSMutableArray and in didSelectCellAtIndexPath: method add or remove the indexPath for that cell to the array and in cellForRowAtIndexPath: check this array and show/hide checkmark base on that table.
dont write this line in cell for row at index path
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
it must be in loadview or viewdidload
I have a problem reloading selected cells.
Trying to follow a tutorial but now stuck with a strange problem.
I have made a list where selecting an item is suppose to show/remove a checkmark.
However, the cell is not updated until I select another cell in the list.
Any ideas?
I have attached the relevant sourcecode below.
Thanks, K
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ListPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
XYZToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem.itemName;
if(toDoItem.completed) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
XYZToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
toDoItem.completed = !toDoItem.completed;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
You've accidentally implemented didDeselectRowAtIndexPath instead of didSelectRowAtIndexPath.
Your problem is tha tha you didn't use the didSelectRowAtIndexPath but the didDeselectRowAtIndexPath so right now every time you deselect your cell you updateyour cell :)
I have a UITableView that has 2 different custom UITableViewCell with 2 different unique identifiers.
I load the first custom UITableViewCell on Load and then the second custom UITableViewCell on Cell Select.
I know that my problem is related to the way I am reusing my Cells. But I've tried using
routineSearchSelectedResultCell * cell = (routineSearchSelectedResultCell*)[tableView dequeueReusableCellWithIdentifier:nil]; and the result is that the new UITableViewCell is empty and the properties do not get populated.
I've also tried [[[cell contentView] subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)]; but give me the same results.
How could I go around this problem??
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (idp && idp.row == indexPath.row ) {
return [self tableViewRoutineSelectedResult:tableView cellForRowAtIndexPath:indexPath];
}
NSString *cellIdentifier = #"routineSearchCell";
routineSearchCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[routineSearchCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
cell.routineName.text = _myDownloadedInfo[#"routines"][indexPath.row][#"routine"][#"routineName"];
cell.routineAuthor.text = _myDownloadedInfo[#"routines"][indexPath.row][#"routine"][#"username"];
return cell;
}
- (routineSearchSelectedResultCell *)tableViewRoutineSelectedResult:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary* myRoutine = _myDownloadedInfo[#"routines"][indexPath.row][#"routine"];
[self sortOutRoutineImages:myRoutine[#"routineType"]];
NSString *cellIdentifier = #"routineSearchSelectedResultCell";
routineSearchSelectedResultCell * cell = (routineSearchSelectedResultCell*)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[routineSearchSelectedResultCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
cell.label1.text =#"test";
cell.contentView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"BlackGradientSearch"]];
cell = [self iconRoutineImagesController:cell];
cell.imgInstruction.image = [UIImage imageNamed:#"InstructionSearchWhite"];
cell.imgVideos.image = [UIImage imageNamed:#"VideoSearchWhite"];
cell.routineName.text = myRoutine[#"routineName"];
[cell.download setBackgroundImage:[UIImage imageNamed:#"DownloadSearchWhite"] forState:UIControlStateNormal];
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ( idp ) {
NSIndexPath* pIdp = [[NSIndexPath alloc] init];
pIdp = idp;
idp = indexPath;
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView reloadRowsAtIndexPaths:#[pIdp] withRowAnimation:UITableViewRowAnimationRight];
[tableView endUpdates];
} else {
idp = [[NSIndexPath alloc]init];
idp = indexPath;
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView endUpdates];
}
}
try to override
-(void)prepareForReuse
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCellWithIdentifier:. For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.
see the document
I think you are using the labels and they are overlapping..
Try to use this, It might help you..
for(UIView *view in cell.subviews){
if([view isKindOfClass:[UILabel class]]){
[view removeFromSuperview];
}
}