I am developing an IOS App. A row consists of an TextField and some textviews. When the value of TextField is changed for any row the number in the TextField should be multiplied with the number in a Label og the same row and displayed in another Label in the same row. What actually happens is that the values are multiplied with the labels of another cell which is apparently random.. The image shows what is actually happening. This problem has me sorely puzzled for days and I am at my wits end.
I Tried Fetching Row From Tableview using didSelectRowAtIndex and
indexPath.row.The Result Are are confusing as row index of first Two rows is being Showns as 0.
IndexPath correct in cellForRowAtIndexPath but is wrong in Textfield Method [ - (void) changedvalue: (UITextField *)textfield ]. What Could Be Worng ?
Any help or advice is greatly appreciated. Thanks in advance.
//Code
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _fetchedobj.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyReuseIdentifier";
UITableViewCell *cell = [myTable dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
data = [_fetchedobj objectAtIndex:indexPath.row];
_quantity = [[UITextField alloc] initWithFrame:CGRectMake(5.0f,28.0f,50.0f , 30.0f)];
[_quantity setTag:indexPath.row];
[_quantity setFont:[UIFont systemFontOfSize:14.0f]];
[_quantity setTextAlignment:NSTextAlignmentCenter];
_quantity.backgroundColor = [UIColor whiteColor];
[_quantity setAdjustsFontSizeToFitWidth:YES];
[_quantity addTarget:self action:#selector(changedvalue:) forControlEvents:UIControlEventAllEditingEvents];
NSString *q_value = [data valueForKey:#"quantity"];
NSInteger q_value1 = [q_value integerValue];
[_quantity setText:[NSString stringWithFormat:#"%li",(long)q_value1]];
[cell addSubview:_quantity];
_total1 = [[UILabel alloc ]initWithFrame:CGRectMake(50.0f, 58.0f, 80.0f, 25.0f)];
[_total1 setFont:[UIFont systemFontOfSize:14.0f]];
[_total1 setTag:1];
[_total1 setTextAlignment:NSTextAlignmentCenter];
[_total1 setAdjustsFontSizeToFitWidth:YES];
_total1.backgroundColor = [UIColor whiteColor];
NSString *totalAmount = [data valueForKey:#"amount"];
NSInteger total_Amount = [totalAmount integerValue];
[_total1 setText:[NSString stringWithFormat:#"%li",(long)total_Amount]];
[cell addSubview:_total1];
- (void) changedvalue: (UITextField *)textfield
{
NSLog(#"%ld",(long)[textfield tag]);
NSString *text = [textfield text];
NSInteger text1 = [text integerValue];
NSString *changevalue = [dict objectForKey:#"_max_purchases_per_user"];
NSInteger valueChange = [changevalue integerValue];
NSString *userPurchase = [dict objectForKey:#"_max_purchases"];
NSInteger itemPurchase = [userPurchase integerValue];
NSString *amount = [data valueForKey:#"amount"];
NSInteger amount1 = [amount integerValue];
NSInteger minValue=MIN(valueChange, itemPurchase);
if (text1 > minValue) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[textfield tag] inSection:0];
UITableViewCell *cell = (UITableViewCell *) [myTable cellForRowAtIndexPath:indexPath];
// UITextField * field=cell.subviews[1];
// field.text = [NSString stringWithFormat:#"%li",minValue * amount1];
NSLog(#"%#",cell);
cell.textLabel.text = [NSString stringWithFormat:#"%li",minValue * amount1];
//[_quantity setText:[NSString stringWithFormat:#"%li",(long)minValue]];
//[ _total1 setText:[NSString stringWithFormat:#"%li",minValue * amount1]];
}else{
[_total1 setText:[NSString stringWithFormat:#"%li",text1 * amount1]];
}
// NSString *Amount =[data valueForKey:#"amount"];
// NSNumber *aNum = [NSNumber numberWithInteger:[Amount integerValue]];
//
// [_total1 setText:[NSString stringWithFormat:#"%ld",([i integerValue] * [aNum integerValue])]];
}
You urgently need to take into account that cells are reused. That's what the "dequeueWithReuseIdentifier" does. So if you have 100 rows, and there are up to 10 visible at a time, you will have 10 rows using the same cell.
You must absolutely NOT create new textfields when dequeueWithReuseIdentifier doesn't return nil. It returns a cell that has already all the textfields created for it, from the previous time that the cell was used for a different or the same row.
And using indexPath.row as the index is a recipe for disaster if the user can add or remove rows. Say you are displaying row 10, then I delete row 8. Your cell now is in row 9, but has a tag that says row 10.
Related
I am trying to create an expandable table view, however when I click on one of the cells I load a .xib file to format the subviews, however when I click the cell again the .xib format remains and messes with the view of the cells. Is there a better way to make the expandable table view?
Code:
- (BOOL)tableView:(UITableView *)tableView canCollapseSection:(NSInteger)section
{
return YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 28;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self tableView:tableView canCollapseSection:section])
{
if ([expandedSections containsIndex:section])
{
return [ticketList count];
}
return 1; // only top row showing
}
// Return the number of rows in the section.
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 72;
}
Load the xib here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"reuse"];
}
// Configure the cell...
NSLog(#"Number: %d", indexPath.section);
NSArray *items = [[NSArray alloc] initWithObjects:#"Cleanliness", #"Door", #"Peek Hole", #"Sink", #"Towel Rack", #"Closet", #"Carpet", #"Wall", #"Bed", #"Matress", #"Mattress Cover", #"Fridge", #"Blinds", #"Window", #"Screen", #"Air Conditioning", #"Chair", #"Desk", #"Garbage bin", #"Shelves", #"Phone", #"Jacks", #"Lights", #"Smoke Detector", #"Heat Detector", #"Light bulb", #"Deep Cleaning", #"Final Prep", nil];
if (!indexPath.row)
{
// first row
cell.textLabel.text = items[indexPath.section]; // only top row showing
if ([expandedSections containsIndex:indexPath.section])
{
}
else
{
}
}
else
{
[self.tableView registerNib:[UINib nibWithNibName:#"HelpDeskCell" bundle:nil] forCellReuseIdentifier:#"Cell"];
static NSString *CellIdentifier = #"Cell";
HelpDeskCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[HelpDeskCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
CGFloat fontsize = 16;
if([[[ticketList objectAtIndex:indexPath.row] objectForKey:#"priority"] isEqualToString:#"Critical"]){
[cell.IDLabel setBackgroundColor:[UIColor colorWithRed:1 green:0.5 blue:0.5 alpha:1.0]];
}
else{
[cell.IDLabel setBackgroundColor:[UIColor colorWithRed:0.925 green:0.925 blue:0.925 alpha:1.0]];
}
[cell.IDLabel setFont:[UIFont boldSystemFontOfSize:fontsize]];
//reduce fontsize to 12 for the information labels
//same on all devices and orientations
fontsize = 12;
//ticket status label
[cell.statusLabel setFont:[UIFont boldSystemFontOfSize:fontsize]];
[cell.statusLabel setTextColor:[UIColor whiteColor]];
//ticket category label
[cell.categoryLabel setBackgroundColor:[UIColor grayColor]];
[cell.categoryLabel setFont:[UIFont boldSystemFontOfSize:fontsize]];
[cell.categoryLabel setTextColor:[UIColor whiteColor]];
//ticket title label
[cell.titleLabel setFont:[UIFont boldSystemFontOfSize:fontsize]];
[cell.titleLabel setTextColor:[UIColor blackColor]];
//Label holds the user that submitted the ticket
[cell.submittedLabel setFont:[UIFont systemFontOfSize:fontsize]];
[cell.submittedLabel setTextColor:[UIColor blackColor]];
//Label holds the user currently working on the ticket
[cell.handleLabel setFont:[UIFont systemFontOfSize:fontsize]];
[cell.handleLabel setTextColor:[UIColor blackColor]];
//ticket date label
[cell.dateLabel setFont:[UIFont systemFontOfSize:fontsize]];
// Set the text of the subviews
NSString * ticketIdStr = [[ticketList objectAtIndex:indexPath.row] objectForKey:#"ticket_id"];
[cell.IDLabel setText:ticketIdStr];
NSString * ticketStatusStr = [[ticketList objectAtIndex:indexPath.row] objectForKey:#"status"];
[cell.statusLabel setText:ticketStatusStr];
if([ticketStatusStr isEqualToString:#"Open"]) {
[cell.statusLabel setBackgroundColor: [UIColor colorWithRed:0 green:0.6 blue:0.8 alpha:1.0]];
}
else if ([ticketStatusStr isEqualToString:#"In Progress"]) {
[cell.statusLabel setBackgroundColor: [UIColor colorWithRed:1.0 green:0.733 blue:0.2 alpha:1.0]];
}
else if ([ticketStatusStr isEqualToString:#"Resolved"]) {
[cell.statusLabel setBackgroundColor: [UIColor colorWithRed:0.6 green:0.8 blue:0 alpha:1.0]];
}
else if ([ticketStatusStr isEqualToString:#"Closed"]) {
[cell.statusLabel setBackgroundColor: [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1.0]];
}
NSString * categoryStr = [[ticketList objectAtIndex:indexPath.row] objectForKey:#"category"];
[cell.categoryLabel setText:categoryStr];
NSString * titleStr = [[ticketList objectAtIndex:indexPath.row] objectForKey:#"title"];
NSString * userIDStr = [[ticketList objectAtIndex:indexPath.row] objectForKey:#"user_id"];
NSString * handledByStr = [[ticketList objectAtIndex:indexPath.row] objectForKey:#"handled_by"];
[cell.titleLabel setText: [NSString stringWithFormat:#"Title: %#", titleStr]];
[cell.submittedLabel setText: [NSString stringWithFormat:#"Submitted By: %#", userIDStr]];
[cell.handleLabel setText: [NSString stringWithFormat:#"Handled By: %#", handledByStr]];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSDate *orignalDate = [dateFormatter dateFromString:[[[ticketList objectAtIndex:indexPath.row] objectForKey:#"date_submitted"] substringToIndex:10]];
[dateFormatter setDateFormat:#"MMMM dd, yyyy"];
NSString * ticketDateStr = [dateFormatter stringFromDate:orignalDate];
[cell.dateLabel setText:ticketDateStr];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
return cell;
}
Expand and Collapse here:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
if (!indexPath.row)
{
// only first row toggles exapand/collapse
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSInteger section = indexPath.section;
BOOL currentlyExpanded = [expandedSections containsIndex:section];
NSInteger rows;
NSMutableArray *tmpArray = [NSMutableArray array];
if (currentlyExpanded)
{
rows = [self tableView:tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
}
else
{
[expandedSections addIndex:section];
rows = [self tableView:tableView numberOfRowsInSection:section];
}
for (int i=1; i<rows; i++)
{
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i
inSection:section];
[tmpArray addObject:tmpIndexPath];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (currentlyExpanded)
{
[tableView deleteRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
}
else
{
[tableView insertRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
}
}
[self.tableView reloadData];
}
}
Update
The main cell is a list of items and the expanding cell is suppose to be tickets from a sql database. I use a xib file to format the tickets
but when I collapse the cell which contains the items, this image remains and covers up the item cells
For creating expandable table view on clicking of UITableViewCell simply add a new cell in existing UITableView
[self.tableview insertRowsAtIndexPath:indexPath
withRowAnimation:UITableViewRowAnimationAutomatic];
Frequently when I pull up on my UITableView to refresh the content drawn from my server it will crash with the following message:
Terminating app due to uncaught exception 'NSRangeException', reason:
'-[__NSCFArray objectAtIndex:]: index (4) beyond bounds (4)'
Now the issue is, my data on my end is set into an NSDictionary with the same data every time, so I know my server is not returning anything that is out of place.
Here how everything is working, I have an integer called i. i will always = 0 at repopulation of cells based on:
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
//end of loading
//for example [activityIndicator stopAnimating];
i = 0;//<!-- reset the i so that when page reloads cells it does not fail
}
}
I am also adding on 2 extra slots for my NSDictionary because slot 0 and 1 are crucial pieces. There associated with user information that populates the first 2 cells:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.dataGrabed_dictionary objectForKey:#"scheduled_times"] count] + 2;//<!-- by default we want to make sure 2 is being added no matter what. 1 = clients address and name, 2 = upcoming lawn care
}
So back to i the first 2 calls to cellForRowAtIndexPath I use if statements to assign the collected data for the first 2 cells. Than I increment i as I populate the other cells with data (if any)
Here is exactly what is going on within cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d",i);
self.tableView.separatorColor = [UIColor clearColor];// Get rid of speration border color
static NSString *CellIdentifier = #"homecell";
HomePageTimelineCell *cell = (HomePageTimelineCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[HomePageTimelineCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell initTimelineCell];
cell.backgroundColor = [UIColor clearColor];// Set the background color to the cell
// lets check if there is any data
if([self.dataGrabed_dictionary objectForKey:#"scheduled_times"] == nil && [self.dataGrabed_dictionary objectForKey:#"upcoming"] == nil)
{
//<!-- if the indexRow row = 0, this is the clients username and address
if(indexPath.row == 0)
{
NSArray *get = [[SSKeychain allAccounts] init];
NSString *username = [get[0] objectForKey:#"acct"];
//<!-- Get keyname of the address and than point to that keyname and get data
//NSString *KeyName = [[self.dataGrabed_dictionary allKeys] objectAtIndex: indexPath.row];
//<!-- create uppercase strings
NSString *upperCaseAddress = [[self.dataGrabed_dictionary objectForKey:#"client_address"] uppercaseString];
NSString *upperCaseUsername = [username uppercaseString];
//<!-- Set associated strings
cell.addressLabel.text = upperCaseAddress;
cell.usernameLabel.text = upperCaseUsername;
}
else if(indexPath.row == 1)
{
cell.contentView.backgroundColor = [self colorWithHexString:#"666666"];
cell.button.backgroundColor = [self colorWithHexString:#"7ddcb8"];
cell.button.tag = indexPath.row;
[cell.button setTitle:#"schedule" forState:UIControlStateNormal];
[cell.button setTitleColor:[self colorWithHexString:#"666666"] forState:UIControlStateNormal];
/* Setup button action */
//[cell.button addTarget:self action:#selector(onCustomAccessoryTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.dateLabel.tag = indexPath.row + 100; //<!-- 100 = date
cell.dateLabel.text = #"Nothing Scheduled";//<!-- Set the Date
[cell.contentView addSubview:cell.button];
[cell.contentView addSubview:cell.nextLabel];
[cell.contentView addSubview:cell.dayLabel];
[cell.contentView addSubview:cell.dateLabel];
}
}
else
{
//<!-- Set background color
if (indexPath.row % 2) {
cell.contentView.backgroundColor = [self colorWithHexString:#"77d1af"];
//<!-- Set background and text color for even cells
[cell.button setTitleColor:[self colorWithHexString:#"7ddcb8"] forState:UIControlStateNormal];
cell.button.backgroundColor = [self colorWithHexString:#"666666"];
} else {
cell.contentView.backgroundColor = [self colorWithHexString:#"7ddcb8"];
//<!-- Set background and text color for even cells
[cell.button setTitleColor:[self colorWithHexString:#"7ddcb8"] forState:UIControlStateNormal];
cell.button.backgroundColor = [self colorWithHexString:#"ffffff"];
}
//<!-- if the indexRow row = 0, this is the clients username and address
if(indexPath.row == 0)
{
NSArray *get = [[SSKeychain allAccounts] init];
NSString *username = [get[0] objectForKey:#"acct"];
//<!-- Get keyname of the address and than point to that keyname and get data
//NSString *KeyName = [[self.dataGrabed_dictionary allKeys] objectAtIndex: indexPath.row + 4];
//<!-- create uppercase strings
NSString *upperCaseAddress = [[self.dataGrabed_dictionary objectForKey:#"client_address"] uppercaseString];
NSString *upperCaseUsername = [username uppercaseString];
//<!-- Set associated strings
cell.addressLabel.text = upperCaseAddress;
cell.usernameLabel.text = upperCaseUsername;
}
//<!-- if the indexRow row = 1, this is the 2 cell and will show the most upcoming lawn schedule
else if(indexPath.row == 1)
{
//<!-- Lets check if user even has an upcoming mowing date -->
if([self.dataGrabed_dictionary objectForKey:#"upcoming"] == nil)
{
cell.contentView.backgroundColor = [self colorWithHexString:#"666666"];
cell.button.backgroundColor = [self colorWithHexString:#"7ddcb8"];
cell.button.tag = indexPath.row;
[cell.button setTitle:#"schedule" forState:UIControlStateNormal];
[cell.button setTitleColor:[self colorWithHexString:#"666666"] forState:UIControlStateNormal];
/* Setup button action */
//[cell.button addTarget:self action:#selector(onCustomAccessoryTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.dateLabel.tag = indexPath.row + 100; //<!-- 100 = date
cell.dateLabel.text = #"Nothing Scheduled";//<!-- Set the Date
[cell.contentView addSubview:cell.button];
[cell.contentView addSubview:cell.nextLabel];
[cell.contentView addSubview:cell.dayLabel];
[cell.contentView addSubview:cell.dateLabel];
}
else
{
//<!-- create uppercase strings
NSString *upperCaseDay = [[self.dataGrabed_dictionary objectForKey:#"upcoming_day"] uppercaseString];
//<!-- create uppercase strings
NSString *upperCaseDate = [[self.dataGrabed_dictionary objectForKey:#"upcoming_date"] uppercaseString];
cell.contentView.backgroundColor = [self colorWithHexString:#"666666"];
cell.button.backgroundColor = [self colorWithHexString:#"7ddcb8"];
cell.button.tag = indexPath.row;
[cell.button setTitle:#"change" forState:UIControlStateNormal];
[cell.button setTitleColor:[self colorWithHexString:#"666666"] forState:UIControlStateNormal];
/* Setup button action */
//[cell.button addTarget:self action:#selector(onCustomAccessoryTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.dayLabel.text = upperCaseDay;//<!-- Set the day
cell.dateLabel.tag = indexPath.row + 100; //<!-- 100 = date
cell.dateLabel.text = upperCaseDate;//<!-- Set the Date
[cell.contentView addSubview:cell.button];
[cell.contentView addSubview:cell.nextLabel];
[cell.contentView addSubview:cell.dayLabel];
[cell.contentView addSubview:cell.dateLabel];
}
}
else //<!-- Normal cell population
{
//<!-- Re edit labels positioning
cell.dayLabel.frame = CGRectMake(9, 10, 200, 45);
cell.dateLabel.frame = CGRectMake(9, 35, 300, 45);
//<!-- Setup data called from server
NSDictionary *innerClientData =[self.dataGrabed_dictionary objectForKey:#"scheduled_times"][i];
NSString *innerClientDay =[self.dataGrabed_dictionary objectForKey:#"scheduled_times_day"][i];
NSString *innerClientDate =[self.dataGrabed_dictionary objectForKey:#"scheduled_times_date"][i];
//<!-- create uppercase strings
NSString *upperCaseDay = [innerClientDay uppercaseString];
//<!-- create uppercase strings
NSString *upperCaseDate = [ innerClientDate uppercaseString];
//<!-- Check to see if client paid
if([[innerClientData objectForKey:#"client_paid"] isEqual: #"0"])
{
NSString *amount = [innerClientData objectForKey:#"client_price"];
NSString *pay = #"pay $";
NSString * combined = [pay stringByAppendingString:amount];
[cell.button setTitle:combined forState:UIControlStateNormal];
}
else if([[innerClientData objectForKey:#"client_paid"] isEqual: #"1"])
{
[cell.button setTitle:#"PAID" forState:UIControlStateNormal];
}
//[cell.button setTitleColor:[self colorWithHexString:#"666666"] forState:UIControlStateNormal];
cell.button.tag = indexPath.row;
/* Setup button action */
//[cell.button addTarget:self action:#selector(onCustomAccessoryTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.dayLabel.text = upperCaseDay;//<!-- Set the day
cell.dateLabel.tag = indexPath.row + 100; //<!-- 100 = date
cell.dateLabel.text = upperCaseDate;//<!-- Set the Date
[cell.contentView addSubview:cell.button];
[cell.contentView addSubview:cell.dayLabel];
[cell.contentView addSubview:cell.dateLabel];
i++;
}
}
return cell;
}
My assigning of the NSDictionary looks like:
NSString *responseString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSLog(#"responseString: %#",responseString);
// GRAB STATUS OBJECT
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:returnData //1
options:kNilOptions
error:&error];
self.dataGrabed_dictionary = [json objectForKey:#"retrieved_data"];
Suggestions as in why it crashes with out of bounds?
The most likely issue is the way you get the data for the cells. You are using the instance variable i on the following lines:
NSDictionary *innerClientData =[self.dataGrabed_dictionary objectForKey:#"scheduled_times"][i];
NSString *innerClientDay =[self.dataGrabed_dictionary objectForKey:#"scheduled_times_day"][i];
NSString *innerClientDate =[self.dataGrabed_dictionary objectForKey:#"scheduled_times_date"][i];
That will never work. Replace i with indexPath.row - 2.
Cells can be accessed multiple times and in various orders.
I've a tableView with dynamic cells. Each section has 5 rows. In the first 4 rows for each row there is a text field. Instead in the fifth row there is an imageview (when I click on this row, I can choose a photo from my photo library, and this last will be put in imageView). The number of sections is decided at run-time using a stepper. The section 0 is fixed and contains a stepper. When I click on + button (on stepper) a section will be add. So everything is right but if I wrote before in the rows that contain the textfield, and then add one or more sections, the contents of these textfield are mixed with each other (and also between sections).
//In file.h I've declared #property (nonatomic) int numberOfComponents;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1 + self.numberOfComponents;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1;
}
else{
return 4;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
NSString *CellIdentifier = #"cellStepper";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *stepperLabel = (UILabel *)[cell viewWithTag:1];
stepperLabel.text = [NSString stringWithFormat:#"%i",self.numberOfComponents];
UIStepper *stepper = (UIStepper *)[cell viewWithTag:2];
stepper.minimumValue = 0;
stepper.maximumValue = 20;
stepper.stepValue = 1;
stepper.autorepeat = YES;
stepper.continuous = YES;
return cell;
}
if (indexPath.row == 4) {
NSString *CellIdentifier = #"cellProfileSnap";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
NSString *CellIdentifier = #"cellDetail";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UITextField *cellLabelComponent = (UITextField *)[cell viewWithTag:3];
cellLabelComponent.placeholder = #"detail";
return cell;
}
- (IBAction)stepperClik:(UIStepper *)stepper{
if (stepper.value == 0 && self.numberOfComponents == 0) {
if (stepper.value > self.numberOfComponents) {
self.numberOfComponents += 1;
}
else{
return;
}
}
if (stepper.value > self.numberOfComponents) {
self.numberOfComponents += 1;
}
else{
self.numberOfComponents -= 1;
}
[self.tableView reloadData];
}
Solved by Greg, but now there's another problem: I have a save button, which should save many arrays into a dictionary (containing the details of each section 5) as the number of sections.
Here the code of save button:
- (IBAction)saveButton:(id)sender{
NSMutableArray *arrComponents = [[NSMutableArray alloc] init];
for (int i = 0; i < self.numberOfComponents; i++)
{
NSMutableArray *component = [[NSMutableArray alloc] init];
for (int j = 0; j < [self.arrDetails count]; j++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:i+1];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath: indexPath];
if (j == 4){
UIImageView *imageComponent = (UIImageView *) [cell viewWithTag:4];
NSLog(#"%#",imageComponent.image);
if (imageComponent.image == Nil) {
[component addObject: nil];
}
[component addObject: imageComponent.image];
}
else{
UITextField *detailComponent = (UITextField *) [cell viewWithTag:3];
NSLog(#"%#",detailComponent.text);
if ([detailComponent.text isEqualToString:#""]) {
[component addObject:#""];
}
if (detailComponent.text != nil && i != 0)
[component addObject: detailComponent.text];
}
}
[arrComponents addObject: component];
NSLog(#"%#",arrComponents);
}
Where it is shown in the code / / ERROR HERE, at the fourth iteration of 5 iterations (number of rows in a section) of the latest iteration (last section read), the application crashes giving this message:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '*** -[__NSArrayM
insertObject:atIndex:]: object cannot be nil'
I believe your detailComponent is equal nil and you cannot save nil to the array. Before you call
[component addObject: detailComponent.text]; //ERROR HERE
do check:
if (detailComponent != nil && i != 0)
[component addObject: detailComponent.text];
It could happen because in your section 0 you haven't got any textfield.
//EDITED
The issue is happened here:
if (imageComponent.image == Nil) {
[component addObject: nil];
}
[component addObject: imageComponent.image];
Replace it with:
if (imageComponent.image == nil) {
[component addObject:[NSNull null]];
}
else {
[component addObject: imageComponent.image];
}
You cannot add nil to array if you want to add nil you should add object NSNull.
And you are missing else statement. Your code try to add nil to the array twice (if the image is nil) first time in your if statement ([component addObject: nil];) and second time just after your if statement: [component addObject: imageComponent.image];
// EDITED
Make the changes as suggested in comments in code below. I use the same dictionary, you should change the name because it suggest that it keeps just textfields values, but now it will store the images as well:
if (indexPath.row == 4) {
NSString *CellIdentifier = #"cellProfileSnap";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//I believe this is where you keep your imageView
// get your image
UIImageView *imageComponent = (UIImageView *) [cell viewWithTag:4];
NSString *key = [NSString stringWithFormat:#"sec:%d,row:%d", indexPath.section, indexPath.row];
if (self.textValueDictionary[key])
imageComponent.image = self.textValueDictionary[key];
else
imageComponent.image = nil; //Your default value (probably you want to set up your plus image)
return cell;
}
The next step you need to make is where the user presses the plus image - you have to save the image in the dictionary. Put this code to your method where you get the image from user. This is a pseudo code so you have to adjust it a little bit:
// Get reference to the cell
UITableViewCell *cell = //have a look on textFieldDidEndEditing method. You have to work out how to get the cell (I cannot do that because I haven't got access to your code);
// Get index path
NSIndexPath *indexPath = [self.myTableView indexPathForCell:cell];
// Get section as a string
NSString *key = [NSString stringWithFormat:#"sec:%d,row:%d", indexPath.section, indexPath.row];
UIImage *img = //get your image here;
//make sure it's not nil before you save it to dictionary
//save it to dictionary
[self.textValueDictionary setValue:img forKey:key];
The last bit is saveButton: method there was mistake in my advices from yesterday. You're trying to get text and image directly from cells, and it works fine if the cells are visible. If you cannot see the cell system put it to reusable pool and you get nils.
To fix it you have to get the data from the dictionary:
Don't use:
UIImageView *imageComponent = (UIImageView *) [cell viewWithTag:4];
instead do:
NSString *key = [NSString stringWithFormat:#"sec:%d,row:%d", indexPath.section, indexPath.row];
UIImage *img = self.textValueDictionary[key];
// make sure is not nil before you save it. If it's nil add [NSNull nil] to your array as I explained before.
When you read text don't do it like that:
UITextField *detailComponent = (UITextField *) [cell viewWithTag:3];
get text from the dictionary:
NSString *key = [NSString stringWithFormat:#"sec:%d,row:%d", indexPath.section, indexPath.row];
NSString *str = self.textValueDictionary[key];
If it complain about data type use cast (UIImage*).
Remember to change your first loop to: for (int i = 1; i < self.numberOfComponents; i++) and when you get NSIndexPath change it to inSection:i instead inSection:i+1.
I haven't run this in Xcode so maybe there are some mistakes but you should be able to find and fix it.
Hope it will work for you.
It happens because you don't save the cellLabelComponent.text property and when you reloadData tableView reuses cell (which cause this problem).
You should save data you entered to your cellLabelComponent (for example in array, you can use UITextFieldDelegate) and in your cellForRowAtIndexPath: method you should assign saved values to desired field.
//EXTENDED
Conform to <UITextFieldDelegate> protocol in your .h file or class extension.
Add
#property (nonatomic, strong) NSMutableDictionary *textValueDictionary;
to your class extension and allocate it and init in viewDidLoad or init method:
self.textValueDictionary = [[NSMutableDictionary alloc] init];
Add this to cellForRowArIndexPath:
cellLabelComponent.placeholder = #"detail";
// Make your class to be delegate for UITextField
cellLabelComponent.delegate = self;
// I use NSString (section) as a key in my dictionary. You can use NSNumber if you like
if (self.textValueDictionary[[NSString stringWithFormat:#"%d", indexPath.section]])
cellLabelComponent.text = self.textValueDictionary[[NSString stringWithFormat:#"%d", indexPath.section]];
else
cellLabelComponent.text = #""; //Your default value
Add your UITextFieldDelegate methods:
- (void)textFieldDidEndEditing:(UITextField *)textField
{
// Get reference to the cell
UITableViewCell *cell = (UITableViewCell*)[[[textField superview] superview] superview];
// Get index path
NSIndexPath *indexPath = [self.myTableView indexPathForCell:cell];
// Get section as a string
NSString *section = [NSString stringWithFormat:#"%d", indexPath.section];
[self.textValueDictionary setValue:textField.text forKey:section];
}
You can use more delegate if you need.
It should be enough to make it works. It will work just when you have one UITextField in your table section if you have more you should use unique key in your dictionary (NSindexPath will work if you have more that one textfield in section but not more that one in row, just remember to convert it to NSNumber).
Let me know is it work.
//EXTENDED
If you have more that one UITextField per section you have to change the dictionary key. This solution above will work just if you have up to one row per section.
This solution (below) will work if you have many textfields per section but not more that one text field per cell (it will work for one textfield per section as well):
Change line in cellForRowAtIndexPath from:
if (self.textValueDictionary[[NSString stringWithFormat:#"%d", indexPath.section]])
cellLabelComponent.text = self.textValueDictionary[[NSString stringWithFormat:#"%d", indexPath.section]];
to:
NSString *key = [NSString stringWithFormat:#"sec:%d,row:%d", indexPath.section, indexPath.row];
if (self.textValueDictionary[key])
cellLabelComponent.text = self.textValueDictionary[key];
And change lines in textFieldDidEndEditing: method from:
NSString *section = [NSString stringWithFormat:#"%d", indexPath.section];
[self.textValueDictionary setValue:textField.text forKey:section];
to:
NSString *key = [NSString stringWithFormat:#"sec:%d,row:%d", indexPath.section, indexPath.row];
[self.textValueDictionary setValue:textField.text forKey:key];
Here is my question:
I have uitableview with different sections and each section have many rows.
The problem is that when I run the app in iPad simulator my first five rows of section 0 in the UITableView does not appear unless I scroll up to see them then they get back to be not appeared after stop scrolling ?
PLEASE HELP .
Thanks in advance and happy coding.
Here is the implementation of Datasource Methods:1. number of sections:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
NSMutableArray *catarr = [[NSMutableArray alloc] init];
catarr= [d Category];
NSInteger length ;
length = [catarr count];
return length;
}
2.number of rows:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSMutableArray *catarr = [[NSMutableArray alloc] init];
catarr = [d Category];
NSInteger length ;
length = [catarr count];
NSInteger count ;
NSMutableArray *word;
for ( int i=0 ; i < length ; i++)
{
if (section == i )
{
NSString *str = [catarr objectAtIndex:i];
word = [[NSMutableArray alloc] init];
word = [d CatRowName:str];
count = [word count];
return count;
}
}
return 0 ;
}
3. cell at index path :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifer = #"Cell";
UITableViewCell *cell= [ tableView dequeueReusableCellWithIdentifier:CellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifer];
}
cell.textLabel.font = [UIFont systemFontOfSize:17.0];
UILabel *label ;
label=(UILabel *)[cell viewWithTag:1];
NSString *value = [[mainArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
label.text = value;
label.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
label=(UILabel *)[cell viewWithTag:2];
NSString *value2 = [[mainArray2 objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
label.text= value2;
NSString *value3 = [[mainArray3 objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:value3];
label.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
return cell;
}
static NSString *cellIdentifier = #"Cell";
NSMutableArray *fields=[[NSMutableArray alloc] init];
MDSpreadViewCell *cell = [aSpreadView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[MDSpreadViewCell alloc] initWithStyle:MDSpreadViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
for (i =0; i < N; i++){
NSLog (#"tag = %i", i);
[fields addObject:[[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)]];
[[fields objectAtIndex:i] setTag:i];
NSString *John = [NSString stringWithFormat:#"%i", i];
[[fields objectAtIndex:i] setText:John];
[[fields objectAtIndex:i] setBackgroundColor:[UIColor redColor]];
[[fields objectAtIndex:i] setDelegate:self];
[[fields objectAtIndex:i] setUserInteractionEnabled:TRUE];
viewWithTag:i]).text;
[cell addSubview:[fields objectAtIndex:i]];
}
return cell;
What is output to the console is correct: Tag = 0, 1, 2, 3, etc., but every cell just says the last value in the array. I've tried putting the for statement in different locations as well as returning the cell in different blocks. I am sure it is something simple that I am overlooking.
This may only be a partially useful answer, since you have some other issues to consider that I don't see addressed in the code snippet you've shown so far (most notably, that you'll need some sort of Model, such as a multidimentional array, that holds the spreadsheet data independently of the table view, and also code that will update the Model when the user changes any of the values in the text fields.)
But I think at this point you are just trying to create textfields in your table cells and get some numbers to appear in them. You might try something like this:
static NSString *cellIdentifier = #"Cell";
MDSpreadViewCell *cell = [aSpreadView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[MDSpreadViewCell alloc] initWithStyle:MDSpreadViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)]];
textField.tag = 1;
textField.backgroundColor = [UIColor redColor];
textField.delegate = self;
[textField setUserInteractionEnabled:YES];
[cell addSubview:textField];
}
UITextField *textFieldForCell = [cell viewWithTag:1];
int numberInCell = rowPath.row + columPath.column;
textFieldForCell.text = [NSString stringWithFormat:#"%i", numberInCell];
return cell;
Just as a future note, it would have been helpful in your original question to indicate that you are using MDSpreadView