Expand / Collapse UITableViewCell with different cell and data source - objective-c

I did a UITableView filled with a plist data source, and I did custom cells with Interface Builder (so I'm using xib files)
here's the part of code where I'm creating cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
DataTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"DataTableViewCell" owner:nil options:nil];
for (UIView *view in views) {
if ([view isKindOfClass:[UITableViewCell class]]) {
cell = (DataTableViewCell*)view;
}
}
}
return cell;
}
then when I use the:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
method to get the selected cell, I call this method to expand the cell:
- (void)expandCellAtIndex:(int)index {
NSMutableArray *path = [NSMutableArray new];
NSArray *currentCells = [subCellItems objectAtIndex:index];
int insertPos = index + 1;
for (int i = 0; i < [currentCells count]; i++) {
[path addObject:[NSIndexPath indexPathForRow:insertPos++ inSection:0]];
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
Now the problem is that since I've not done this before, I'm stuck on the logic on how to change the cell I want to show when expanded, that's because the method:
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
is searching for the right path and inserting a cell with the same cell xib file as the one I've loaded with my data at the start
how can I set a different cell (with a different xib, and different data) to show instead of the same as before?
basically I have this table working but in the expanding cell I see a copy of the cell you touch

You can do it like this:
In .h:
#interface TSViewController : UIViewController
{
NSMutableArray* subCellIndexPaths;
}
In DataTableViewSubCell.xib create one "Table View Cell" (other Views you have to remove). Change Class for "Custom Class" to DataTableViewSubCell. Replay it for second cell.
In .m:
- (UITableViewCell*) tableView: (UITableView*) tableView
cellForRowAtIndexPath: (NSIndexPath*) indexPath
{
static NSString *CellIdentifier = #"Cell";
DataTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
if ([subCellIndexPaths containsObject: indexPath])
{
NSArray* nib = [[NSBundle mainBundle] loadNibNamed: #"DataTableViewSuperCell"
owner: nil
options: nil];
cell = [nib objectAtIndex: 0];
}
else
{
NSArray* nib = [[NSBundle mainBundle] loadNibNamed: #"DataTableViewSubCell"
owner: nil
options: nil];
cell = [nib objectAtIndex: 0];
}
}
return cell;
}
- (void) expandCellAtIndex: (int)index
{
NSMutableArray* indexPaths = [NSMutableArray new];
NSArray* currentCells = [subCellItems objectAtIndex: index];
int insertPos = index + 1;
for (int i = 0; i < [currentCells count]; i++)
{
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: insertPos++
inSection: 0];
[indexPaths addObject: indexPath];
[subCellIndexPaths addObject: indexPath];
}
[self.tableView insertRowsAtIndexPaths: indexPaths
withRowAnimation: UITableViewRowAnimationFade];
[self.tableView scrollToRowAtIndexPath: [NSIndexPath indexPathForRow: index
inSection: 0]
atScrollPosition: UITableViewScrollPositionTop
animated: YES];
}
And of course you must create subCellIndexPaths anywhere (in init or viewDodLoad methods).

Related

Accessory checkmarks disappear when scrolling Objective-C

I have a tableview that I can add and remove multiple checkmarks. The only issue is if I put 3 checkmarks and scroll away, when I return the checkmarks are gone. I can't find anywhere on the internet a solution that works, and I've tried several variation and still nothing.
This is my code in cellForRowAtIndex that should be holding the checkmarks in place.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseIdentifier = #"contactCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
NSDictionary *contact = [self.tableData objectAtIndex:indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:1];
NSString *firstName = contact[#"firstName"];
nameLabel.text = [firstName stringByAppendingString:[NSString stringWithFormat:#" %#", contact[#"lastName"]]];
UILabel *phoneNumber = (UILabel *)[cell viewWithTag:2];
NSArray *phones = contact[#"phones"];
if ([phones count] > 0) {
NSDictionary *phoneItem = phones[0];
phoneNumber.text = phoneItem[#"value"];
}
UIImageView *cellIconView = (UIImageView *)[cell.contentView viewWithTag:888];
UIImage *image = contact[#"image"];
cellIconView.image = (image != nil) ? image : [UIImage imageNamed:#"smiley-face"];
cellIconView.contentScaleFactor = UIViewContentModeScaleAspectFill;
cellIconView.layer.cornerRadius = CGRectGetHeight(cellIconView.frame) / 2;
// Need to fix
if([checkedIndexPath isEqual:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Here is the didSelectRowAtIndexPath method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell* checkCell = [tableView cellForRowAtIndexPath:indexPath];
if(checkCell.accessoryType == UITableViewCellAccessoryCheckmark)
{
checkCell.accessoryType = UITableViewCellAccessoryNone;
NSMutableArray *i = [[NSMutableArray alloc] init];
for (NSIndexPath *indexPath in [self.tableView indexPathsForSelectedRows]) {
[i addObject:self.tableData[indexPath.row]];
// Go inside pull the numbers from the users and save in an NSArray
// NSArray *contacts = i;
// self.recipients = [[NSMutableArray alloc] init];
for (NSDictionary* dict in i) {
// Grab phones
NSDictionary *contactNumber = [dict objectForKey:#"phones"];
for (NSDictionary* dict2 in contactNumber) {
// Grabs the phone numbers
NSString* value = [dict2 objectForKey:#"value"];
int index = [self.recipients indexOfObject:value];
[self.recipients removeObjectAtIndex:index];
[self.selectedUsers removeObjectAtIndex:index];
NSLog(#"The number that has a checkmark%#", value);
NSLog(#"the array of all%#", self.recipients);
NSLog(#"At index %lu", (unsigned long)[self.recipients indexOfObject:value]);
// [_recipients addObject:value];
}
}
// NSLog(#"Phone Numbers: %#",_recipients);
}
}
else
{
[self getNumber];
NSLog(#"clicking %#", self.recipients);
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
checkedIndexPath = indexPath;
}
}
I found The Solution:
You must save each indexPath into an array(put this code in didSelectRowAtIndexPath) and then in cellForRowAtIndexPath add the following code
if([self.checkedCells containsObject:indexPath]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
Also in the didSelectRowAtIndexPath
Make sure to delete the indexPath when deselecting the row.
if(checkCell.accessoryType == UITableViewCellAccessoryCheckmark) {
checkCell.accessoryType = UITableViewCellAccessoryNone;
[self.checkedCells removeObject:indexPath];
I hope this helps someone. I been wrestling with this all day.
Make checkedIndexPath a #property (nonatomic, strong) and use self.checkedIndexPath whenever you refer to it. You're losing the reference after didSelectRowAtIndexPath exits. Set a breakpoint in cellForRowAtIndexPath and look at checkedIndexPath, I bet it's nil.
Maybe you should check if the isEqual functionality does what you expect. You could make sure by trying:
if (_checkedIndexPath.section == indexPath.section &&
_checkedIndexPath.row == indexPath.row)
If you still do not get the expected result, perhaps log the values of section and row to see where it goes wrong.
Please note that if for some reason _checkedIndexPath is a weak variable or gets deallocated, this check will fail.
You could also check that your cells are properly dequeued before being modified and that you are returning the correct cells.
If you want to store more than one checked row, of course, you will need more than one indexPath variable (just one _checkedIndexPath will not do it).

iOS:Selection and saving data or print the data when row is selected in table view

Iam using the did select method to select the data .so data in a particular row is selecting and aim getting the check mark also now my problem is when the check mark is on state the data should be printed in console and if its off the data should be removed from the array. my code is as below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
NSLog(#"array is %#",array);
static NSString *CustomCellID = #"cell";
contactcellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CustomCellID];
if (cell == nil)
{
cell=[[contactcellTableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CustomCellID];
NSArray *nib=[[NSBundle mainBundle]loadNibNamed:#"contactcellTableViewCell" owner:self options:nil];
cell =[nib objectAtIndex:0];
}
cell.Firstnamelbl.text = [[array objectAtIndex:indexPath.row] objectForKey:#"first_name"];
cell.Lastnamelbl.text=[[array objectAtIndex:indexPath.row]objectForKey:#"last_name"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[array removeAllObjects];
contactcellTableViewCell *cell = (contactcellTableViewCell *) [tableView cellForRowAtIndexPath:indexPath];
static NSString *CustomCellID = #"cell";
if (cell == nil)
{
cell=[[contactcellTableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CustomCellID];
NSArray *nib=[[NSBundle mainBundle]loadNibNamed:#"contactcellTableViewCell" owner:self options:nil];
cell =[nib objectAtIndex:0];
}
if (cell.m_checkImageView.image == [UIImage imageNamed:#"Selected.png"])
cell.m_checkImageView.image = [UIImage imageNamed:#"Unselected.png"];
else
cell.m_checkImageView.image = [UIImage imageNamed:#"Selected.png"];
}
I had two labels in cell so that when the particular row is selected the data should be print on the console
i got the answer please find below
if (cell.m_checkImageView.image == [UIImage imageNamed:#"Selected.png"])
{
cell.m_checkImageView.image = [UIImage imageNamed:#"Unselected.png"];
}
else
{
cell.m_checkImageView.image = [UIImage imageNamed:#"Selected.png"];
NSLog(#"string is %#",string);
}
}
now we will see the data when row is selected

Two tableviews in one view doesn't work

I have two UITableviews in my view, delegates and datasources of these two table are connected to File's Owner. I have two IBOutles connected to my UITableView:
IBOutlet UITableView *tableOne;
IBOutlet UITableView *tableTwo;
In my case, the information contained in tableOne are being passed to tableTwo, I believe the problem is inside the method cellForRowAtIndexPath as you can see below:
-(void)viewDidAppear:(BOOL)animated{
[tableOne reloadData];
[tableTwo reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (tableView == tableOne) {
return 3;
}else{//Else is the tableTwo
return 3;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if (tableView == tableOne) {
static NSString *simpleTableIdentifier = #"ContasAlertaTableViewCell";
ContasAlertaTableViewCell *cell = (ContasAlertaTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ContasAlertaTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.data.text = [date objectAtIndex:indexPath.row];
return cell;
}else{// Else is tableTwo
static NSString *simpleTableIdentifier = #"FaltasTableViewCell";
FaltasTableViewCell *cell = (FaltasTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"FaltasTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.data.text = [date2 objectAtIndex:indexPath.row];
return cell;
}
}
If you notice I'm using different table cells for each of the tables, and I believe that would be what is influencing this error, in my case, What I have to do to solve this type of problem?

How to pass an object when a button is clicked

I have a custom tableview cell which has a button. I need to pass the NSIndexPath object of tablview when the button is clicked. I am able to do is assign a tag to a button, receive the tag using sender..but I want to pass the NSIndexPath object...below is the code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"shortCodeCell";
IPadTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"KeywordsCell" owner:nil options:nil];
for (id currentObject in topLevelObjects)
{
if ([currentObject isKindOfClass:[IPadTableViewCell class]])
{
cell = (IPadTableViewCell *)currentObject;
}
}
}
// Delete
[cell.btnDelete addTarget:self action:#selector(onDelete:) forControlEvents:UIControlEventTouchUpInside];
cell.btnDelete.tag=indexPath.row;
return cell;
}
-(void)onDelete:(id)sender
{
UIButton *btn=(UIButton *)sender;
NSLog(#"BtnIndexpath to be deleted:%d",btn.tag);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
int errorCode = 0;
kd = [items objectAtIndex:btn.tag];
BOOL isDeleteKeyword= [ServerAPI deleteKeywordWithId:kd.keywordId :&errorCode];
dispatch_async (dispatch_get_main_queue (),
^{
if (isDeleteKeyword) {
[items removeObjectAtIndex:btn.tag];
//[tblKeywords deleteRowsAtIndexPaths:[NSArray arrayWithObject:btnIndexPath] withRowAnimation:YES];
[self.tblKeywords reloadData];
}
else return;
});
});
}
You can set the following in "cellForRowAtIndexPath"
cell.tag = indexPath.section;
cell.btnDelete.tag=indexPath.row;
And in "onDelete" you can create indexPath based on tags assigned.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[sender tag] inSection:[[sender superview] tag]]
// I assume your UITableView called tableView
// Using this way you don't need to use tag
-(void)onDelete:(id)sender
{
UITableViewCell *cell = (UITableViewCell *)[sender superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
}

Receive Data from UITableViewCell

I have the following code set:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"customCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nibObjs = [[NSBundle mainBundle] loadNibNamed:#"CustomCellView" owner:nil options:nil];
//cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
for(id currentObj in nibObjs)
{
if([currentObj isKindOfClass:[CustomCell class]])
{
cell = (CustomCell *)currentObj;
}
}
}
AssessmentDetail * anAssess = [module.assessmentDetails4 objectAtIndex:indexPath.row];
[[cell labelAssessment] setText:anAssess.assessmentName4];
return cell;
}
In my custom cell there is a UISlider. What I would like to do is use a button to retrieve the value of each UISlider from each row so I can get a total value.
I thought about doing this but I'm not certain on where to go from there:
- (IBAction) calculateTotal:(id)sender {
static NSString *CellIdentifier = #"customCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
Thanks for the help.
Cycle through the subviews of the UITableView:
- (IBAction) calculateTotal:(id)sender {
NSArray *subViews = [myTableView subviews];
float total;
for (int i = 0; i < subViews.count; i++)
{
id obj = [subViews objectAtIndex:i];
if ([obj isKindOfClass:[CustomCell class]])
{
total += [[obj getMySlider] getValue];
}
}
// do something with total
}