Using some API, I am displaying data in table view format.
A) In that when API is called first time we will get 10 user details, so at first time we can see 10 rows in tableview. When we scroll down i.e after 10 rows, a new API called for nextPageURL i.e page 2 and it contains gain 10 user details. Again when you reach 20 row after again nextPageURL i.e page 3 API will call and again 10 records will get in JSON and again it will displayed in tableview. (This is working fine. There is no issue while getting data and displaying in data) This is the working flow my tableview in my project.
B) Here I am using UILongPressGestureRecognizer for selecting rows of tableview. Using UILongPressGestureRecognizer I can able to select multiple rows. (This is also working fine)
C) Code used for it, selecting and deselecting tableview row
#interface InboxViewController ()
{
NSMutableArray *selectedArray;
NSString *selectedIDs;
}
#property (strong,nonatomic) NSIndexPath *selectedPath;
- (void)viewDidLoad
{
selectedArray = [[NSMutableArray alloc] init];
self.tableView.allowsMultipleSelectionDuringEditing = true;
UILongPressGestureRecognizer *lpGesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(EditTableView:)];
[lpGesture setMinimumPressDuration:1];
[self.tableView addGestureRecognizer:lpGesture];
[self reload]; // for getting data
}
-(void)reload
{
// API sample
NSString * url= [NSString stringWithFormat:#"%#api/v2/get-userDetails?token=%#&api=%#&show=%#&departments=%#",[userDefaults objectForKey:#"baseURL"],[userDefaults objectForKey:#"token"],apiValue,showInbox,Alldeparatments];
NSLog(#"URL is : %#",url);
// here get JSON (First 10 user details data)
}
-(void)EditTableView:(UIGestureRecognizer*)gesture{
[self.tableView setEditing:YES animated:YES];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.currentPage == self.totalPages
|| self.totalTickets == _mutableArray.count) {
return _mutableArray.count;
}
return _mutableArray.count + 1;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == [_mutableArray count] - 1 ) {
NSLog(#"nextURL111 %#",_nextPageUrl);
if (( ![_nextPageUrl isEqual:[NSNull null]] ) && ( [_nextPageUrl length] != 0 )) {
[self loadMore]; // this method is called for getting next data i.e getting next 10 user details
}
else{
NSLog (#"ALL Caught UP");
}
}
this is for first API call and here I will get 10 user details and I am displaying in tableview.
For getting next user details following method is called
-(void)loadMore
{
// next page API called here
}
for selecting row I am using following,
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 3;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
Selecting and deselecting rows
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
self.selectedPath = indexPath;
if ([tableView isEditing]) {
// [selectedArray addObject:[_mutableArray objectAtIndex:indexPath.row]];
[selectedArray addObject:[[_mutableArray objectAtIndex:indexPath.row] valueForKey:#"id"]];
count1=(int)[selectedArray count];
NSLog(#"Selected count is :%i",count1);
NSLog(#"Slected Array Id : %#",selectedArray);
selectedIDs = [selectedArray componentsJoinedByString:#","];
NSLog(#"Slected Ticket Id are : %#",selectedIDs);
}else{
// goes to next detail view
}
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedPath = indexPath;
// [selectedArray removeObject:[_mutableArray objectAtIndex:indexPath.row]];
[selectedArray removeObject:[[_mutableArray objectAtIndex:indexPath.row] valueForKey:#"id"]];
count1=(int)[selectedArray count];
NSLog(#"Selected count is :%i",count1);
NSLog(#"Slected Id : %#",selectedArray);
selectedIDs = [selectedArray componentsJoinedByString:#","];
NSLog(#"Slected Ticket Id are : %#",selectedIDs);
if (!selectedArray.count) {
[self.tableView setEditing:NO animated:YES];
}
}
My Problem/Issue -
I am selecting row of tableview using UILongPressGestureRecognizer, up to 10 rows (it is in front end) and in background in one array its id is storing. If you select some rows, its row id will add in selectedArray if you deselect row it will remove object from selectedArray
Now suppose I selected 5 tickets and suppose when I scroll down (after 10 rows) new API will call, and next 10 userdetails will display, but this time whatever selected rows are vanishing (selected rows are showing unselected) but still in background there id is stored.
What I want is that, when I select some rows even I scroll down and goes to any page, that selected arrow will not vanish and that that selected row is stored in selectedArray object
If you're using multiple selection, you can add this method to your ViewController and call it whenever you need to call [tableView reloadData] to preserve the selection.
- (void)reloadTableView
{
NSArray *indexPaths = [self.tableView indexPathsForSelectedRows];
[self.tableView reloadData];
for (NSIndexPath *path in indexPaths) {
[self.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
Referred from save selected row in UITableView after reloadData
Related
I declared globally
NSIndexPath *selectedIndexPath;
and here is my didselectmethod
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
UIAlertView *loAlert = [[UIAlertView alloc]initWithTitle:#"Are you sure you want to redeem?" message:nil delegate:self cancelButtonTitle:#"Yes" otherButtonTitles:#"No", nil];
loAlert.tag = 10;
[loAlert show];
selectedIndexPath = indexPath;
}
After selecting row i am showing alert
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: (NSInteger)buttonIndex
{
if(alertView.tag == 10)
{
if(buttonIndex == 0)
{
[self redeemOfferService];
}
}
}
Then calling redeemOfferService, in this method i am facing problem
- (void)redeemOfferService
{
OffersBoughtCellTableViewCell * selectedCell1 = [offersTableView cellForRowAtIndexPath:selectedIndexPath];
NSLog(#"%# ************", selectedCell1.quantity.text);
// for e.g. selected cell's quantity is 0 (here i can see selected cell quantity)
i want to change and update here as follow
selectedCell1.quantity.text = #"Quantity : 5";
// after this i can see quantity in my cell is 5 but
when i scroll values are again showing from
cellForRowAtIndexPath i want to show updated value on cell,
how to do this ?
}
For more details here is my cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
OffersBoughtCellTableViewCell *cell = (OffersBoughtCellTableViewCell *)
[tableView dequeueReusableCellWithIdentifier:#"offersCell" forIndexPath:indexPath];
OfferDetail * offerDetailObj=[[OfferDetail alloc]init];
// OfferDetail, OffersBoughtDetails are NSObject class
// #property (nonatomic, strong) OfferDetail *offerObj;& OffersBoughtDetails * selectedOfferBoughtObj;
// and #property (nonatomic, strong) NSArray *offersBought;
offerDetailObj=[self.listOfOffers objectAtIndex:indexPath.row];
OffersBoughtDetails * offerBoughtObj=[self.offerObj.offersBought objectAtIndex:indexPath.row];
selectedOfferBoughtObj=offerBoughtObj;
cell.quantity.text=[NSString stringWithFormat:#"Quantity : %#",offerBoughtObj.quantity];
}
The problem is that when you scroll to your changed cell again, - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath get invoked, but your data would not change,cell.quantity.text=[NSString stringWithFormat:#"Quantity : %#",offerBoughtObj.quantity];, so your cell's quantity still shows what your data model offerBoughtObj is.
Since iOS uses MVC, a solution is you change your data model, and then reflect your model changes to your view.
- (void)redeemOfferService {
OffersBoughtDetails * offerBoughtObj=[self.offerObj.offersBought objectAtIndex:selectedIndexPath];
// change offerBoughtObj to the value you want
offerBoughtObj.quantity = whateveryoulike;
// reload your tableview
[tablview reloadRowsAtIndexPaths:#[selectedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
As far As I understand your problem after selecting a Cell You want to remove that cell from UITableView.
To do this put all your UITable data in NSMutableArray
And In your - (void)redeemOfferService method after doing all task
just remove that selected object like:
[dataArray removeObject:#"selectedObject"];
And reload the table Data
[tableView reloadData];
You have to update the source of the table, the offerDetailObj in your case, so in - (void)redeemOfferService methods, fetch the data from the array and get the selected object, update it's value and save it again to your array. I think this should work:
- (void)redeemOfferService
{
OffersBoughtCellTableViewCell * selectedCell1 = [offersTableView cellForRowAtIndexPath:selectedIndexPath];
NSLog(#"%# ************", selectedCell1.quantity.text);
// for e.g. selected cell's quantity is 0 (here i can see selected cell quantity)
//i want to change and update here as follow
selectedCell1.quantity.text = #"Quantity : 5";
//Update your source array to the correct value
offerDetailObj=[self.listOfOffers objectAtIndex:indexPath.row];
OffersBoughtDetails * offerBoughtObj=[self.offerDetailObj.offersBought objectAtIndex:indexPath.row];
OffersBoughtDetails.quantity = 5;
[offerDetailObj replaceObjectAtIndex:indexPath.row withObject:offerBoughtObj];
self.listOfOffers replaceObjectAtIndex:indexPath.row withObject:offerDetailObj];
}
Your Array should be mutable so you can add/remove/replace object from it, means that your listOfOffers and offersBought should be NSMutableArray.
I think in your alertViewDelegate method you should set selectetdIndexPath to nil if the user selects NO
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: (NSInteger)buttonIndex
{
if(alertView.tag == 10)
{
if(buttonIndex == 0)
{
[self redeemOfferService];
}
else
selectedIndexPath = nil;
}
}
and in your redeemOffService you should reload cell
- (void)redeemOfferService
{
OffersBoughtCellTableViewCell * selectedCell1 = [offersTableView cellForRowAtIndexPath:selectedIndexPath];
NSLog(#"%# ************", selectedCell1.quantity.text);
// for e.g. selected cell's quantity is 0 (here i can see selected cell quantity)
i want to change and update here as follow
selectedCell1.quantity.text = #"Quantity : 5";
[self.tableView reloadRowsAtIndexPaths:#[selectedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
selectedIndexPath = nil;
}
Hope this helps :D
I am trying to prevent the delete for every last row in every section of my UITableView control. I wrote the code which is working so far.
Is there a way to prevent the delete button from appearing for a specific row in a specific section when in edit mode of UITableView?
Here is the code :
- (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger section = indexPath.section;
NSInteger row = indexPath.row;
// If table view is asking to commit a delete command...
if ( editingStyle == UITableViewCellEditingStyleDelete) {
// Prevent deleting the last row
int length = [[[MyItemStore sharedStore] getItemsForGivenSection:section] count];
// PREVENT LAST ROW FROM DELETING
if ( row == length) {
return;
}
NSArray *items = [[MyItemStore sharedStore] getItemsForGivenSection:section];
MyItem *item = items[row];
[[MyItemStore sharedStore] removeItemFromSection:item fromSection:section];
//Also remove that row from Table view with animation
[tableView deleteRowsAtIndexPaths: #[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
You could use something like this:
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
//Return FALSE for the last row
if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section] - 1)
return FALSE;
}
//Return TRUE for all other rows
return TRUE;
}
You can do this but tableView:commitEditingStyle:forRowAtIndexPath is too late as this gets called when the user already triggered the deletion. You can either return UITableViewCellEditingStyleNone from tableView:editingStyleForRowAtIndexPath: or set the editingStyle property of the UITableViewCell. Sounds like you want to go with the former and return none if it's the last row in each section.
You can use this code in the Table View Controller:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section] - 1)
return UITableViewCellEditingStyleNone;
}
return UITableViewCellEditingStyleDelete;
}
I have a UITableView having many rows that contains UITextFields. User enters data to these fields. user can delete or add any rows. When i am trying to delete any row it deletes the correct row from the array(that contains all the cell's reference) but the UITextfield always shows that the last row is deleted.
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _countEmailValues.count ;
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CustomEmailCell";
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[LACustomEmailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.emailTextField.tag = 555;
cell.deleteEmailFieldButton.tag = indexPath.row;
NSLog(#"delete email Field tag %i",cell.deleteEmailFieldButton.tag );
cell.emailTextField.delegate = self;
if ([_countEmailValues count] > 1 )
{
cell.deleteEmailFieldButton.hidden = false;
}
else
{
cell.deleteEmailFieldButton.hidden = true;
}
// Reason why I am adding cell.emailtextfield in this delegate? is should be in addButtonclick but cell.emailtextfield is not
// initialized there. Also adding of only cell will give null for the emailTextField.
// So adding all cells here and then removing duplicates entires and accessing over the app.
[_emailValues addObject:cell.emailTextField];
// Remove Duplicate elements.
NSArray *emailFieldCollection = [_emailValues copy];
NSInteger index = [emailFieldCollection count ] -1;
for (id object in [emailFieldCollection reverseObjectEnumerator])
{
if ([_emailValues indexOfObject:object inRange:NSMakeRange(0, index)] != NSNotFound)
{
[_emailValues removeObjectAtIndex:index];
}
index--;
}
NSLog(#"Row : %i",indexPath.row);
return cell;
}
- (IBAction)deleteEmailClick:(UIButton *)sender
{
NSIndexPath *index = self.emailTableView.indexPathForSelectedRow;
// [self.emailTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0] animated:YES scrollPosition:0];
NSIndexPath *indexPath = [_emailTableView indexPathForSelectedRow];
NSLog(#"Tags %i", sender.tag);
// [self.emailTableView reloadData];
if ([_countEmailValues count] > 0)
{
[ _countEmailValues removeObjectAtIndex:sender.tag];
}
// NSLog(#"array after %#",_countEmailValues);
if ([_countEmailValues count] == 0)
{
// _deleteEmailButton.hidden = true;
_doneButton.hidden = true;
}
NSLog(#"array before %#",_emailValues);
if ([_emailValues count] > 0)
{
[_emailValues removeObjectAtIndex:sender.tag];
}
[self.emailTableView reloadData];
}
The _emailValues gets updated properly but the data in the fields is always getting cleared of the last one.
EX: in the image if i delete "b" the _emailValues gets cleared properly but the ui shows two field having data "a" & "b" . what have i missed here?
pls help.
You are not deleting the cell . In deleteEmailClick add this line
[self.emailTableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
Also I would like to point out another way of implementing delete using a block. This will ensure that you always have the correct cell and no need to tag your delete button. This would be cleaner and maintainable. In your custom cell class LACustomEmailCell declare a block property like so
#property (strong, nonatomic) void (^deleteButtonTappedBlock)();
Connect the IBAction of your delete button in the LACustomEmailCell class and from there call this block. I am assuming the name as deleteButtonPressed
- (IBAction)deleteButtonPressed:(id)sender {
self.deleteButtonTappedBlock();
}
Now in -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
You can set the block like this
[cell setDeleteButtonTappedBlock:^(){
//Action to perform when cell is deleted. In your case the code in `deleteEmailClick` will go here.
//In the end delete the cell.
[self.emailTableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
i hope this helps
I have a tableView with with some sections and each section with some rows.Every row has a favorite button.If the button is clicked the row should get added to the favourites section.
(which is initially empty).
I have written some code.but the problem is it is working in iOS 5 simulator and getting crashed in iOS 6 simulator with Invalid tableView update error.
Can someone tell me where the problem is.?
-(void)moveRowToFavourites:(id)sender{
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:[NSIndexPath indexPathForRow:favouritesArray.count inSection:0]];
[favouritesArray insertObject:cell.textLabel.text atIndex:favouritesArray.count];
[[self tableView] beginUpdates];
[[self tableView] insertRowsAtIndexPaths:(NSArray *)tempArray withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] endUpdates];
}
EDIT
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(searching){
return [self.searchResults count];
}
else{
if ([[arrayForBool objectAtIndex:section] boolValue]) {
if(section == 0){
return favouritesArray.count;
}else{
NSString* key = [self.proCategoriesArray objectAtIndex:section - 1];
NSArray *aArray = [sectionContentDict valueForKey:key];
return aArray.count;
}
}
return 1;
}
}
You just should call the following:
[favouritesArray addObject:cell.textLabel.text];
[tableView reloadData];
Make sure you implemented this function:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
switch(section){
case 0:
return favouritesArray.count;
//Your other sections have to placed here, too
default:
return 0;
}
}
Then your tableView should update itself. The problem is that you insert whole cells, but you just have to insert data in your array. Hope it helps!
It seems like your data source is returning 0 as number of sections and rows. You are not correctly inserting/deleting rows, when you insert a new row, data source methods get called again, and if for instance you try to insert an object in a way that the row count will be 4, and data source tableView:numberOfRowsInSection: returns 3, you get an exception because you're trying yo add more objects that what data source promises.
When you try to update a table view all these data source methods get called again:
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView;
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath;
You must ensure that they return correct values. I suspect that you aren't even implementing them.
Edit
The problem is that you are just inserting a new object in favouritesArray, but this doesn't affect the number of rows returned by tableView:numberOfRowsInSection:. There you aren't returning favouritesArray.count, the exception is due to the fact that tableView:numberOfRowsInSection: returns a lower number than the rows that the table view should have.
In your case I think that you don't even need to call insertRowsAtIndexPaths:, you can do everything with your data source:
-(void)moveRowToFavourites:(id)sender {
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
[favouritesArray insertObject:cell.textLabel.text atIndex:favouritesArray.count];
[[self tableView]reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return favouritesArray.count;
}
In tableView:cellForRowAtIndexPath: you should return a cell that has an object fetched form favouritesArray as text.
I'm building a app which has a table view. But I want this to be a table view which expands the cell when you tap on it and close when you tap a second time.
But I was wondering if the following is possible. When the cell isn't selected you only see a picture, title and the beginning of the text. But as soon as the cell is selected, it will expand and show even more subviews, i.e. image views.
Is this possible? For instance, to hide a subview in the cell, and as soon a it is tapped it's visible and aligned the right way? And of course, how do i do that?
Thnx!!!
I did something similar quite a few time ago. You'll find the code at github.
Note that it is very rough, as it where my beginning iPhone days, i.e. properties are missing.
.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
NSIndexPath *selectedIndexPath;
NSDictionary *articles;
}
#end
.m
#import "FirstViewController.h"
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
selectedIndexPath = nil;
articles = [[NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:#"one", #"two", #"three",
#"four", #"five", #"six",
#"seven", #"eight", #"nine",
#"ten", #"eleven", nil]
forKey:#"title"] retain];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[selectedIndexPath release];
[articles release];
[super dealloc];
}
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[articles allKeys] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[articles allKeys] objectAtIndex : section];
}
- (int)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
id key = [[articles allKeys] objectAtIndex:section];
return [[articles objectForKey : key] count];
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ((selectedIndexPath != nil) && (selectedIndexPath.row == indexPath.row))
return 80.0;
return 40.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * MyIdentifier = #"MyIdentifier";
UITableViewCell * cell = [self.tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
id key = [[articles allKeys] objectAtIndex:indexPath.section];
cell.textLabel.text = [[articles objectForKey:key] objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedIndexPath == indexPath) {
selectedIndexPath = nil;
} else {
selectedIndexPath = indexPath;
}
[self.tableView deselectRowAtIndexPath : indexPath animated : NO];
[tableView beginUpdates];
[tableView endUpdates];
}
#end
Yes, I do this in the app I'm working on now.
You need to keep track of the state that a cell is in, either open or closed. If only 1 cell can be open at a time, you can do this by just keeping a reference to the current indexPath. If multiple cells can be open at the same time, you'll need an array of booleans, which tracks if each one is open or closed.
In heightForRowAtIndexPath, just return the correct height based on if the row is open or closed.
In cellForRowAtIndexPath, if the row is closed, hide all the content that shouldn't be visible when it's closed. The views can still be there, but they should be set to hidden = YES.
Finally, in didSelectRowAtIndexPath, set the given index path to open if it was closed, and closed if it was open, then reload the cell with [tableView reloadRowsAtIndexPaths:]. If you are only allowing 1 at a time to be open, then just set your current open index path to the one that was selected, and reload both the one that was selected as well as the one that had been previously open.