Changing the default TableViewCell to custom subclass on button click - uitableview

In a specific row I apply the default UITableViewCell class with a text label and an accessory button. When the accessory button is clicked, the cell is expanded and I want to change the UITableViewCell to a custom subclass I have created. However even if the cell is expanded, it won't switch to the custom subclass. Any ideas what to fix?
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat result;
switch ([indexPath row])
{
case 2:
{
if ([indexPath isEqual:expandedRow]) {
return 100;
} else {
return 42;
}
}
return result;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier;
NSString *CellIdentifierexp;
UITableViewCell *cell;
if (cell == nil) {
if (indexPath.row == 2) {
if ([indexPath isEqual:expandedRow]) {
cell = [[ExpandedCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierexp];
}else{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
switch ([indexPath row])
{
case 2:
{
if ([indexPath isEqual:expandedRow]) {
NSLog(#"bike");
ExpandedCell *expandedcell = (ExpandedCell *)cell;
[expandedcell.text setText:self.descr];
NSArray *indexPathArray=[NSArray arrayWithObject:indexPath];
[self.tableview reloadRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone];
} else {
cell.textLabel.text = #"Description";
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.textColor = UIColorFromRGB(0x2d5b3b);
// accessory type image
UIImage *image = [UIImage imageNamed:#"greenarrow.jpg"]; //or wherever you take your image from
UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(0,0, image.size.width, image.size.height)];
[button setBackgroundImage:image forState:UIControlStateNormal];
[button addTarget:self action:#selector(accessoryButtonTapped:withEvent:) forControlEvents:UIControlEventTouchDown];
button.userInteractionEnabled = YES;
cell.accessoryView = button;
}
break;
}
return cell;
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
switch ([indexPath row])
{
case 2:
{
NSLog(#"Detail Disclosure Tapped");
expandedRow = indexPath;
[tableview beginUpdates];
[tableview endUpdates];
}
}
}
- (void) accessoryButtonTapped: (UIControl *) button withEvent: (UIEvent *) event
{
NSIndexPath * indexPath = [self.tableview indexPathForRowAtPoint: [[[event touchesForView: button] anyObject] locationInView: tableview]];
if ( indexPath == nil )
return;
[self.tableview.delegate tableView: self.tableview accessoryButtonTappedForRowWithIndexPath: indexPath];
}

Wow, you're doing something terribly wrong in here:
ExpandedCell *expandedcell = (ExpandedCell *)cell;
[expandedcell.text setText:self.descr];
NSArray *indexPathArray=[NSArray arrayWithObject:indexPath];
[self.tableview reloadRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone];
What you should do is slightly different. On a button tap you just call the [self.tableview reloadRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone]; then in the cellForRowAtIndexPath you should return the custom cell of your subclass in the method if the indexPath matches. No need to update the tableView there.
And need I say that this looks like a very very strange switch statement to me:
switch ([indexPath row])
{
case 2:
{
NSLog(#"Detail Disclosure Tapped");
expandedRow = indexPath;
[tableview beginUpdates];
[tableview endUpdates];
}
}
I would simply put it like this:
if (indexPath.row == 2){
NSLog(#"Detail Disclosure Tapped");
expandedRow = indexPath;
[tableview beginUpdates];
[self.tableview reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableview endUpdates];
}

Running the code
expandedRow = indexPath;
[tableview beginUpdates];
[tableview endUpdates];
when the accessory button is tapped will only ask the table view to update the row heights, it won't actually request a new cell for any row, so no cell type change will happen (until you scroll the row off screen and then back on again).
So, you need to actually reload the selected row, not just begin and end updates.

this here...
UITableViewCell *cell;
if (cell == nil) {
Local vars dont init to nil , they start as garbage, maybe nil , maybe not.
Either way you probably are not making it into the branch.
You are going to want to dequeue a cell from the table depending on whether its an expanded row or not
UITableViewCell *cell = nil;
if(thisIsAnExpandedRow) {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierexp];
if(!cell) {
cell = [[ExpandedCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierexp];
}
}
else {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
}

Related

Obj-C - Deselect the selected cell when another cell is tapped?

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];
}

How To Get TableviewCell Selected Check Mark Row Values Using Objective C?

I need to create tableviewcell checkmark selected row title label and detail label values get after clicking the submit button.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil )
{
cell =[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
if ([indexPath compare:self.lastIndexPath] == NSOrderedSame)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
// UITableView Delegate Method
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"Section:%ld Row:%ld selected and its data is %# %#",(long)indexPath.section,(long)indexPath.row,cell.sector_Label.text,cell.textLabel.text);
selectedIndex = indexPath.row;
[tableView reloadData];
}
- (IBAction)Submit_Click:(id)sender {
// Here I need to get tableview cell check mark selected cell label values.
}
Check this:
-(IBAction)Submit_Click:(id)sender {
// Here I need to get tableview cell check mark selected cell label values.
if (selectedIndex >= 0) {
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:selectedIndex inSection:0];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"detailTextLabel: %#", cell.detailTextLabel.text);
NSLog(#"title: %#", cell.textLabel.text);
}
}
You can use indexPathsForSelectedRows
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
cell = [tableView cellForRowAtIndexPath:indexPath];
if(cell.isSelected)
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
else
{
[cell setSelected:YES animated:YES]
}
}
-(IBAction)Submit_Click:(id)sender {
// Here I need to get tableview cell check mark selected cell label values.
NSArray *indexes = [self.tableview indexPathsForSelectedRows];
if (indexes.count > 0)
{
//do your stuff as you get selected row index array
}
}

expanded UITableviewCell height not manageable

I am using this in my app and i am create two identifiers for UITableview one is "normal cell" and another one is "expended cell" which will show when someone tap on a "normal cell". my problem is this that I can not manage heights of both cells separately for example I want normal cell height to be 120 and extended cell is to be 60. But if I change height in heightForRowAtIndexPath then both cells showing same height. here is my code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FSParallaxTableViewCell *cell = nil;
static NSString *cellIdentifier = nil;
if ([indexPath isEqual:self.expandedIndexPath]) {
cellIdentifier = #"ExpandedCell";
}
else {
cellIdentifier = #"NormalCell";
}
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[FSParallaxTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if ([[cell reuseIdentifier] isEqualToString:#"ExpandedCell"]) {
cell.contentView.backgroundColor = [UIColor grayColor];
}
if ([[cell reuseIdentifier] isEqualToString:#"NormalCell"]) {
[cell.cellImageView sd_setImageWithURL:[NSURL URLWithString:[[rssOutputData objectAtIndex:indexPath.row]xmllink]]
placeholderImage:[UIImage imageNamed:#"placeholder"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];
cell.clipsToBounds = YES;
}
return cell;
}
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// disable touch on expanded cell
UITableViewCell *cell = [self.theTableView cellForRowAtIndexPath:indexPath];
if ([[cell reuseIdentifier] isEqualToString:#"ExpandedCell"]) {
return;
}
// deselect row
[tableView deselectRowAtIndexPath:indexPath
animated:NO];
// get the actual index path
indexPath = [self actualIndexPathForTappedIndexPath:indexPath];
// save the expanded cell to delete it later
NSIndexPath *theExpandedIndexPath = self.expandedIndexPath;
// same row tapped twice - get rid of the expanded cell
if ([indexPath isEqual:self.expandingIndexPath]) {
self.expandingIndexPath = nil;
self.expandedIndexPath = nil;
}
// add the expanded cell
else {
self.expandingIndexPath = indexPath;
self.expandedIndexPath = [NSIndexPath indexPathForRow:[indexPath row] + 1
inSection:[indexPath section]];
}
[tableView beginUpdates];
if (theExpandedIndexPath) {
[theTableView deleteRowsAtIndexPaths:#[theExpandedIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
if (self.expandedIndexPath) {
[theTableView insertRowsAtIndexPaths:#[self.expandedIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
[tableView endUpdates];
// scroll to the expanded cell
[self.theTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle
animated:YES];
}
can anyone please help me resolving this issue I want to set height for both cells separately.
add below code in heightForAtIndexpath method
if ([indexPath isEqual:self.expandedIndexPath]) {
return 60.0;
}
else
return 120.0f;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
}
if ([[cell reuseIdentifier] isEqualToString:#"ExpandedCell"]) {
return 60;
}
if ([[cell reuseIdentifier] isEqualToString:#"NormalCell"]) {
return 120;
}
}
You have to set the height of table view cell separately. you can set the height separately according as the row.In following code i have set the 1st row height 35, 2nd row height 70 and rest of the row will have height 65.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row)
{
case 0:
return 35;
break;
case 1:
return 70;
break;
default:
return 65;
break;
}
}

Hide or move seleted Cell and load a new custom Cell in that indexPath.row in UItableView

I have a UITableView call myTableView with two custom UITableViewCell call TableViewCell & ExTableViewCell. What I want is, when user tap on a cell, the existing cell TableViewCell will hide/move and ExTableViewCell is loaded in that indexpath.row and when tap on that indexpath.row again it hide ExTableViewCell and bring back the old TableViewCell in that position.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.myArray = [[NSArray alloc] initWithObjects:#"one", #"two", #"three", #"four", #"five", #"six", nil];
selectedIndex = -1;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedIndex == indexPath.row)
{
return 230;
}
else
{
return 40;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.myArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
TableViewCell *Cell = (TableViewCell *)[self.myTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!Cell)
{
Cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Cell.myLabel.text = [self.myArray objectAtIndex:indexPath.row];
if (selectedIndex == indexPath.row)
{
static NSString *CellIdentifier = #"CellEx";
ExTableViewCell *Cell = (ExTableViewCell *)[self.myTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!Cell)
{
Cell = [[ExTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Cell.backgroundColor = [UIColor redColor];
Cell.exLabel.text = [self.myArray objectAtIndex:indexPath.row];
}
else
{
// Do close cell stuff
//Cell.backgroundColor = [UIColor clearColor];
}
return Cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Expand row when user taps row
if (selectedIndex == indexPath.row)
{
selectedIndex = -1;
[self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation: UITableViewRowAnimationFade];
return;
}
// When user taps different row
if (selectedIndex != -1)
{
NSIndexPath *prevPath = [NSIndexPath indexPathForRow:selectedIndex inSection:0];
selectedIndex = indexPath.row;
[self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:prevPath] withRowAnimation:UITableViewRowAnimationFade];
}
// When user taps new row with none expanded
selectedIndex = indexPath.row;
[self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
But for some reason in the ExTableViewCell label is not showing any text. And the ExTableViewCell is still top of it. How can I achieve this?
A lot a thanks for advance. Have a good day. :)
This is the out put:
My problem:
You don't need to 'hide' the old cell in order to show the new one, you just reload the proper content at the desired index path. Something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if ([self.selectedPath isEqual:indexPath]) {
//configure the extended cell
cell = [tableView dequeueReusableCellWithIdentifier:#"CellEx" forIndexPath:indexPath];
...
} else {
//configure the default cell
}
}
And here is how to handle the selected/deselected state:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSIndexPath *oldPath = [self.selectedPath copy];
self.selectedPath = indexPath;
NSArray *paths = #[indexPath];
if (oldPath && ![oldPath isEqual:indexPath]) {
paths = [paths arrayByAddingObject:oldPath];
} else if ([oldPath isEqual:indexPath]){
self.selectedPath = nil;
}
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}

UITableViewCell content of previous row is still visible on Cell reload

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];
}
}

Resources