ios - Expand table view cell like Twitter app - ios

I want to have the same behavior as Twitter app when selecting a tweet that is : expanding the row and adding supplementary content.
So this is not only a basic row resize. I think i need 2 different custom cells, so i did something like that :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath compare:self.selectedIndexPath] != NSOrderedSame) {
FirstCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FirstCell"];
if (!cell) {
cell = [[FirstCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"FirstCell"];
}
cell.firstnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"firstname"];
return cell;
}
else {
SecondCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SecondCell"];
if (!cell) {
cell = [[SecondCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SecondCell"];
}
cell.firstnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"firstname"];
cell.lastnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"lastname"];
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath compare:self.selectedIndexPath] == NSOrderedSame) {
return 80.0;
} else {
return 44.0;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedIndexPath = indexPath;
[tableView reloadData];
}
My problem here is that i want to keep a fluid animation like Twitter app which is not my case because of [tableView reloadData] (which is mandatory i think since i have 2 different custom cell).
So anyone knows a workaround to my needed or maybe someone knows how Twitter app is handling this animation stuff ?

You want to reload that cell with an animation:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];

[tableView beginUpdates];
[tableView endUpdates];
this triggers an update of the cell row sizes for the whole tableView.... referenced from :iPhone - Smooth animation for UITableViewCell height change including content update
Happy Coding!

It is actually rather easy to implement this using two custom cells like you originally intended by just combining your original code with rooster117's solution. Replace [tableView reloadData] in your tableView:didSelectRowAtIndexPath: with:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
and you should have your solution.

Related

Can any one tell how to implement expandable/collapse tableview

Here is my screen and code i have attached below.can any one give idea how to implement the screen thanks in advance..in my header section i need show only the tableview cell.........................................
[1]: http://i.stack.imgur.com/EVlQs.png
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
return [arr count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellidentifier =#"cell";
MyOrderCell *cell = (MyOrderCell *)[tableView dequeueReusableCellWithIdentifier:cellidentifier];
/********** If the section supposed to be closed *******************/
if(cell==nil)
{
cell = [[MyOrderCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifier];
}
if (selectedindex==indexPath.row) {
// do expanded stuff here
cell.lblcope.text=[[pendentsdata objectAtIndex:indexPath.row]valueForKey:#"pendents"];
cell.lblcopieces.text=[[pendentsdata objectAtIndex:indexPath.row]valueForKey:#"copiece"];
cell.lblconum.text=[[pendentsdata objectAtIndex:indexPath.row]valueForKey:#"conum"];
cell.lblcodate.text=[[pendentsdata objectAtIndex:indexPath.row]valueForKey:#"codate"];
cell.lblcoenddate.text=[[pendentsdata objectAtIndex:indexPath.row]valueForKey:#"coenddate"];
cell.lbltotl.text=[[pendentsdata objectAtIndex:indexPath.row]valueForKey:#"cototal"];
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
}else
{
// do closed stuff here
cell.lblpendnts.text=[arr objectAtIndex:indexPath.row];
cell.lblpieces.text=[arrpieces objectAtIndex:indexPath.row];
cell.lblnum.text=[arrnum objectAtIndex:indexPath.row];
cell.lbldate.text=[arrdate objectAtIndex:indexPath.row];
cell.lblenddate.text=[arrenddate objectAtIndex:indexPath.row];
cell.lbltotl.text=[arrttl objectAtIndex:indexPath.row];
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
}
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedindex==indexPath.row) {
selectedindex=-1;
[tbldata reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
return;
}
if (selectedindex !=-1) {
NSIndexPath *prevpath=[NSIndexPath indexPathForRow:selectedindex inSection:0];
selectedindex=indexPath.row;
[tbldata reloadRowsAtIndexPaths:[NSArray arrayWithObject:prevpath] withRowAnimation:UITableViewRowAnimationFade];
}
selectedindex=indexPath.row;
[tbldata reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedindex==indexPath.row) {
return 100;
}else
{
return 50;
}
}
You can use https://github.com/iSofTom/STCollapseTableView
I have used this in a project. The code written is very clean, so you can also easily customise it to cater your specific needs if any.
You can use SubTable, a third party subclass of UITableView. Besides same questions as yours are below, check them out:
How to create an Accordion with UItableview under a UItableview?
Accordion table cell - for expand and collapse ios
Indeed Burak, or take tutorial...
an example: https://blog.pivotal.io/labs/labs/expandable-uitableviewcells

Why UITableView is not dequeuing reusable cells?

I'm adding subviews with UITableViewCell default type. In this, assume that, I've only 2 rows. At first, when it comes to tableView:cellForRowAtIndexPath: I'll generate a dynamic identifier for each cell based on indexPath.row. So it goes well and create a new cell inside if(cell == nil) condition. I'm adding subviews inside this condition only.
Next, out side that condition, I'm simply updating content of subview by fetching them out of the cell's contentView. This works great.
Problem is here : I'm expanding my cell (on which user tapped). When I do this, it'll go into tableView:cellForRowAtIndexPath: and I'm able to see the same identifier generated before. However, its going inside if(cell == nil) condition.
For a note, I'm checking for that condition like this,
if(!cell) and not if(cell == nil).
Can you guess what is the issue? However in my deep investigation, I found that, it'll only goes inside if(cell == nil) once I expand particular row and not next time for the same row.
Here's the sample :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//number of count
return [array count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//dynamic height will be provided based on content.
return [self heightForRow:indexPath];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = [NSString stringWithFormat:#"cellIdentifier_%ld", (long)indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[self setupCell:cell forRow:[indexPath row] inTable:tableView];
}
[self updateCell:cell forRow:[indexPath row] inTable:tableView];
return cell;
}
- (void) setupCell:(UITableViewCell *)cell forRow:(NSInteger)row inTable:(UITableView *)tableView {
//setting up view here
}
- (void) updateCell:(UITableViewCell *)cell forRow:(NSInteger)row inTable:(UITableView *)tableView {
//updating view here
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.expandedIndexPath = ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) ? nil : indexPath;
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
[tableView scrollToRowAtIndexPath:self.expandedIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
Hope my issue is clear to you. Please comment if you don't get something or found anything missing here (and I'll update it here) or answer if you know what's the solution to this.
Thanks!

UITableView cell's seperators disappear when selected

When I select a UITableViewCell and start scrolling to the point where the cells disappear from the screen, the seperators on those cells disappear once they reappear on the screen.
Delegate:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if(cell == nil){
if(Type == Scene){
Group *scene = [[appdata getScenes]objectAtIndex:lastRequestedPropertyPosition];
[selectedArray addObject:scene.id];
}else if(Type == Product){
Device *device = [[appdata getDevices]objectAtIndex:lastRequestedPropertyPosition];
[selectedArray addObject:device.id];
}
}else{
[selectedArray addObject:[NSNumber numberWithInt:cell.tag]];
}
}
Datasource:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"protoCell" forIndexPath:indexPath];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"protoCell"];
}
Group *group = [[appdata getScenes] objectAtIndex:indexPath.row];
if(group != nil){
[cell.textLabel setText:group.name];
}
cell.tag = group.id.intValue;
return cell;
}
Can anyone tell what went wrong here?
Thanks
EDIT
Using this just makes the separator disappear on select instead of just keeping the separator there.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if(cell == nil){
if(Type == Scene){
Group *scene = [[appdata getScenes]objectAtIndex:lastRequestedPropertyPosition];
[selectedArray addObject:scene.id];
}else if(Type == Product){
Device *device = [[appdata getDevices]objectAtIndex:lastRequestedPropertyPosition];
[selectedArray addObject:device.id];
}
}else{
[selectedArray addObject:[NSNumber numberWithInt:cell.tag]];
}
}
The cause of my issue was the fact I used a blue color shade to show a cell being selected. This shade however was a little too big. It was covering the seperator
In tableView:didSelectRowAtIndexPath, if you send both messages below, the issue is visually resolved (with the probable performance cost).
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
For this to work (for me), deselectRowAtIndexPath:animated: must contain animated:YES. The animation used for reloadRowsAtIndexPaths:withRowAnimation: doesn't matter.

UITableViewCell not updating after selection on iOS 7

So I am having a really weird issue. My code works great on iOS 8 but doesn't on iOS 7 and I can't figure out why.
I have a tableview which has a list of items which when you select an item a checkmark is added to that item and if you select it again the checkmark is removed. Pretty simple right? :-p Like I said it works great on iOS 8, but when I run against iOS 7.1 the cell highlights, adds a checkmark, and removes the old title and replaces it with the default of Title. Afterwards, no matter how many times I tap on the cell, it never changes (but the underlying data does change).
Before Selection
After Selection
If I leave the screen and come back to it, the rows are displayed properly. I've verified that cellForRowAtIndexPath is being called and the correct values are being added to the cell.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [[parkFinderSingleton.data valueForKey:#"amenities"] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"amenityFilterCell" forIndexPath:indexPath];
Amenity *currentAmenity;
NSArray *amenities = [parkFinderSingleton.data valueForKey:#"amenities"];
if (amenities != nil) {
currentAmenity = amenities[indexPath.row];
cell.textLabel.text = currentAmenity.amenityTitle;
NSLog(#"Cell Title %#", cell.textLabel.text);
if (currentAmenity.amenitySelected) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"amenityFilterCell" forIndexPath:indexPath];
Amenity *currentAmenity;
NSArray *amenities = [parkFinderSingleton.data valueForKey:#"amenities"];
if (amenities != nil) {
currentAmenity = amenities[indexPath.row];
if (currentAmenity.amenitySelected) {
currentAmenity.amenitySelected = NO;
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
currentAmenity.amenitySelected = YES;
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
Any thoughts as to what might be happening?
Normally, dequeueReusableCellWithIdentifier:forIndexPath: should not be called in tableView: didSelectRowAtIndexPath:. If you want the cell for a specific indexPath, use [tableView cellForRowAtIndexPath:indexPath].
For:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
If you just want to deselect a cell, use [tableView deselectRowAtIndexPath:animated:].

iOS UITableView reloadRowsAtIndexPaths

I have a UITableview that lazy loads images of all different sizes. When an image loads, I need to update the specific cell, so I figured out I need to use reloadRowsAtIndexPaths. But when I use this method, it still calls the heightForRowAtIndexPath method for every single cell. I thought the whole purpose of reloadRowsAtIndexPaths is that it will only call heightForRowAtIndexPath for the specific row you specify?
Any idea why?
[self.messageTableView beginUpdates];
[self.messageTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:count inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.messageTableView endUpdates];
Thank You
endUpdates triggers a content size recalculation, which requires heightForRowAtIndexPath. That's just how it works.
If it's a problem, you could pull your cell configuration logic outside of cellForRowAtIndexPath and reconfigure the cell directly without going through reloadRowsAtIndexPaths. Here is a basic outline for what this could look like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellId = ...;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
[self tableView:tableView configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)tableView:(UITableView *)tableView configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
//cell configuration logic here
}
Then, wherever you're currently calling reloadRowsAtIndexPaths, you do this instead and heightForRowAtIndexPath won't be called:
UITableViewCell *cell = [self.messageTableView cellForRowAtIndexPath:indexPath];
[self tableView:self.messageTableView configureCell:cell atIndexPath:indexPath];

Resources