0

So i have been trying for a little while now to create a table view with expandable sections and one non expandable section. One of the expandable sections should have 3 text fields inside them in which you can edit the test inside the text field. I was able to get that working bt the moment you collapse the section and expand it again the textfield suddenly duplicates itself above and sometimes swaps itself out with the above cell. Ihavent been able to figure out why or how to make it not do that. The idea is when the user enters text in the field and selects enter the text is stored into an array.

the code:

    - (void)viewDidLoad{
    [super viewDidLoad];

    if (!expandedSections){
        expandedSections = [[NSMutableIndexSet alloc] init];
    }
    manualSensorName = [[NSMutableArray alloc]initWithObjects: @"Sensor",@"",@"2",@"T", nil];
}

- (void)didReceiveMemoryWarning{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Expanding

- (BOOL)tableView:(UITableView *)tableView canCollapseSection:(NSInteger)section{
    if (section>0) return YES;

    return NO;
}


#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if ([self tableView:tableView canCollapseSection:section])
    {
        if ([expandedSections containsIndex:section])
        {
            return 5; // return rows when expanded
        }

        return 1; // only top row showing
    }

    // Return the number of rows in the section.
    return 1;
}

- (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:CellIdentifier];
    }

    // Configure the cell...

    if ([self tableView:tableView canCollapseSection:indexPath.section]){
        if (!indexPath.row){
            // first row
            cell.textLabel.text = @"Expandable"; // only top row showing

            if ([expandedSections containsIndex:indexPath.section])
            {
                UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrowUP.png"]];
                cell.accessoryView = arrow;
            }
            else
            {
                UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrowDOWN.png"]];
                cell.accessoryView = arrow;
            }
        }
        else {
            if (indexPath.row == 1){
                NSString *flightNumText = [manualSensorName objectAtIndex:indexPath.row];
                cell.textLabel.text = flightNumText;
            }
            else if (indexPath.row == 2){
                txtManualSensor = [[UITextField alloc] initWithFrame:CGRectMake(180, 5, 120, 30)];
                txtManualSensor.placeholder = @"Select";
                txtManualSensor.delegate = self;
                txtManualSensor.autocorrectionType = UITextAutocorrectionTypeNo;
                txtManualSensor.backgroundColor = [UIColor whiteColor];
                [txtManualSensor setBorderStyle:UITextBorderStyleBezel];
                [txtManualSensor setTextAlignment:NSTextAlignmentCenter];
                [txtManualSensor setReturnKeyType:UIReturnKeyDone];
//                UITextField *playerTextField = [[UITextField alloc] initWithFrame:CGRectMake(180, 5, 120, 30)];
//                playerTextField.adjustsFontSizeToFitWidth = YES;
//                playerTextField.textColor = [UIColor blackColor];
//                playerTextField.placeholder = @"SAMPLE";
//                playerTextField.tag = 200;
//                playerTextField.delegate = self;
//                [cell.contentView addSubview:playerTextField];
                cell.textLabel.text = @"Sensor Name";
                [cell addSubview:txtManualSensor];
            }
            else if (indexPath.row == 3){
                cell.textLabel.text = @"Some Detail";
                cell.accessoryView = nil;
                cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
            }

        }
    }
    else {
        cell.accessoryView = nil;
        cell.textLabel.text = @"Normal Cell";

    }

    return cell;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    [manualSensorName replaceObjectAtIndex:2 withObject:textField.text];
    return YES;
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField{

    [textField resignFirstResponder];
    return YES;
}

// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}


#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    if ([self tableView:tableView canCollapseSection:indexPath.section]){
        if (!indexPath.row){
            [tableView beginUpdates];

            // 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) {
                UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrowDOWN.png"]];
                [tableView deleteRowsAtIndexPaths:tmpArray
                                 withRowAnimation:UITableViewRowAnimationFade];
                cell.accessoryView = arrow;
            }
            else {
                UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrowUP.png"]];
                [tableView insertRowsAtIndexPaths:tmpArray
                                 withRowAnimation:UITableViewRowAnimationFade];
                cell.accessoryView =  arrow;
            }
            NSLog(@"tableview row is %ld in section %ld",(long)indexPath.row,(long)indexPath.section);
            [tableView endUpdates];
        }
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        NSLog(@"selected row is %ld in section %ld",(long)indexPath.row,(long)indexPath.section);
        if (indexPath.row == 1) {
            // update text fields in cell table view
        }
    }
}
fluxingjoe
  • 48
  • 9
  • Your question is possibly answered here, with a demo: http://stackoverflow.com/a/34997679/218152 – SwiftArchitect Feb 06 '16 at 06:20
  • That answer was written in Swift and i dont have reloadRowsAtIndexPaths on the tableView as i have tried to insert that code snippet before. – fluxingjoe Feb 06 '16 at 16:11

1 Answers1

0

It may be as simple as replacing UITableViewRowAnimationTop by UITableViewRowAnimationFade:
When changing indexes in didSelectRowAtIndexPath, UITableViewCells change physical location (remember that the UITableView is a UIScrollView), and the scroller can't keep track of what your intent is.

UITableViewRowAnimationTop attempts to adjust the scrolling location, but fails.


Other design considerations:

  • Do not mix the model (the array of data to be displayed) with your view model (the UI displaying the model). In didSelectRowAtIndexPath, you should first re-order your model, then apply it to the cells

  • Consider not changing indexes on the fly: you may prefer a model that actually reflects the view structure, i.e. a tree.

  • Have you noticed you are not respecting - (void)tableView:(UITableView *)tableView and sometimes using self tableView:tableView or self.customTableView in the same method? You should use the tableView passed to you.

SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
  • i made those changes that you suggested however the behavior is still happening – fluxingjoe Feb 06 '16 at 16:05
  • however i did not understand exactly what you meant in your first bullet-ed suggestion so i did not try that one – fluxingjoe Feb 06 '16 at 16:14
  • so what i ended up doing was moving all the cellForAtIndexPath off into a custom cell subclass and it helped to alleviate the problem. – fluxingjoe Feb 08 '16 at 15:29