-1

I'm facing problem with saving UITableViewCell's state and can't figure out how to solve it. Hope somebody can help me.

Explanation: There is an API on server and I get data from it and then store it inside NSMutableArray. Each object of an array contains property ready which can be 1 or 0. So I've no problems with populating UITableView with this data but not every data object is ready (i.e 0) and I need to get progress of completion at server and after that to show it in each cell is need it. I've UIProgressView in dynamic prototype of UITableViewCell and set progress after getting. There is no problem if such "not ready" object is only one. But if there are many objects I can't show progress and I don't understand why.

So here is my code.

cellForRowAtIndexPath method:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"readyCell";
AVMMovieCell  *cell = [self.readyTable dequeueReusableCellWithIdentifier:CellIdentifier];
    // Configure the cell...
if (cell == nil) {
    cell = (AVMMovieCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
AVMFilmsStore *oneItem;
oneItem = [readyArray objectAtIndex:indexPath.row];
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)indexPath.row];

if (oneItem.ready==1){
    cell.progressLabel.hidden = YES;
    cell.progressLine.hidden = YES;

if ([selecedCellsArray containsObject:[NSString stringWithFormat:@"%@",rowNsNum]]  )
{

    if (![cell.progressLabel.text isEqualToString:@""]&& ![cell.progressLabel.text isEqualToString:@"Success"] && ![cell.progressLabel.text isEqualToString:@"Creating"]){
        cell.progressLabel.hidden = NO;
        cell.progressLine.hidden = NO;
    } else {
        cell.progressLabel.hidden = YES;
        cell.progressLine.hidden = YES;
      }
}
else{
    if(!oneItem.isProcessing && !cell.selected){
    cell.progressLabel.hidden = YES;
    cell.progressLine.hidden = YES;
    }
}
} else { //if processing
if (![processingCellsArray containsObject:[NSString stringWithFormat:@"%@",rowNsNum]]){
    [processingCellsArray addObject:[NSString stringWithFormat:@"%@",rowNsNum]];
    if (!cell.isSelected){

        [cell setSelected:YES];

    }
    cell.progressLabel.hidden = NO;
    cell.progressLine.hidden = NO;

    NSArray * arrayOfThingsIWantToPassAlong =
    [NSArray arrayWithObjects: cell, oneItem, indexPath, nil];

    if(!isMaking){
        [self performSelector:@selector(getProgress:)
                   withObject:arrayOfThingsIWantToPassAlong
                   afterDelay:0];
    } else{
    [self performSelector:@selector(getProgress:)
               withObject:arrayOfThingsIWantToPassAlong
               afterDelay:0.5];
    }

    isMaking = YES;

} else {
    if (!cell.isSelected){
        [cell setSelected:YES];
    }

    cell.progressLabel.hidden = NO;
    cell.progressLine.hidden = NO;

    NSArray * arrayOfThingsIWantToPassAlong =
    [NSArray arrayWithObjects: cell, oneItem, indexPath, nil];

    if(!isMaking){
        [self performSelector:@selector(getProgress:)
                   withObject:arrayOfThingsIWantToPassAlong
                   afterDelay:0];
    } else{
    [self performSelector:@selector(getProgress:)
               withObject:arrayOfThingsIWantToPassAlong
               afterDelay:0.3];
    }


    isMaking = YES;
    }

   }
    return cell;
 }

and getProgress method:

-(void)getProgress:(NSArray*)args{

if (progManager == nil && !progStop){
    __block AVMFilmsStore * oneItem = args[1];
    if(!oneItem.isLocal){
        __block AVMMovieCell * cell = args[0];
        __block NSIndexPath *indexPath = args[2];
        progManager = [AFHTTPRequestOperationManager manager];
        __block NSString *token = [defaults objectForKey:@"token"];
        __block NSString *header = [NSString stringWithFormat:@"Bearer %@",token];
        __block NSDictionary *params = @{@"lang": NSLocalizedString(@"lang",nil),@"project":oneItem.fileId};
        __block NSString *oneHundredPercent;
        __block NSString *progIsText;

        progManager.responseSerializer = [AFJSONResponseSerializer serializer];
        [progManager.requestSerializer setValue:header forHTTPHeaderField:@"Authorization"];
        if(cell.selected || isMaking) { //if I just check for "cell.selected" is always "NO"
            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
            [progManager POST:@"http://example.com/api/project/get-progress" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
                if ([[responseObject objectForKey:@"result"]isEqualToString:@"success"]){
                    progCreate = [responseObject objectForKey:@"progress"];

                    oneHundredPercent = @"100";
                    if ([progCreate intValue]==[oneHundredPercent intValue]){
                        if([processingCellsArray containsObject:[NSString stringWithFormat:@"%ld",(long)indexPath.row]]){
                            [processingCellsArray removeObject:[NSString stringWithFormat:@"%ld",(long)indexPath.row]];
                            [cell setSelected:NO];

                        }
                        [readyArray removeAllObjects];
                        [defaults setObject:@"false" forKey:@"isSomethingInSort"];
                        isMaking = NO;
                        [self getReadyMovies:progIsText nameLabel:oneItem.fileName];

                    } else{
                        if([progCreate intValue]>=50){
                            if([progCreate intValue]>=60){
                                self.navigationController.navigationItem.leftBarButtonItem.enabled = YES;
                               createMainButton.enabled = YES;
                            }
                            [[NSNotificationCenter defaultCenter] postNotificationName:@"gotFiftyNote" object:@"50"];
                            [cell.progressLine setProgress:[progCreate floatValue]/100 animated:YES];
                        } else {
                            [cell.progressLine setProgress:progUploadLimit];
                        }
                        progManager = nil;
                        progManager.responseSerializer = nil;
                        progManager.requestSerializer = nil;
                        token = nil;
                        header = nil;
                        params = nil;
                        progIsText = nil;
                        oneItem = nil;
                        cell = nil;
                        indexPath = nil;
                        isMaking = YES;
                        progCreate = nil;
                        oneHundredPercent = nil;

                        [self getProgress:args];
                    }
                }
            } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
                NSLog(@"Error: %@", error.localizedDescription);
            }];

        }

    }
  }
}

Any suggestions will be helpful for me. I've a headache for two weeks with this problem.

vendettacore
  • 1,439
  • 1
  • 13
  • 28
  • 1
    You need to refactor this code and embrace object orientation. Make the cell responsible for updating itself. `performSelector` is almost always a code smell. You will also be having issues because you are updating the UI from a background handler and potential issues with non-threadsafe updates to NSMutableArray – Paulw11 Mar 27 '15 at 09:40
  • I had same issue a few days ago. Check Rob's comment and Aaron's example. It may help you: [link](http://stackoverflow.com/questions/29202212/uiprogressview-on-uitableviewcell) – TienLe Mar 27 '15 at 11:13

1 Answers1

0

I see your code but is kind of difficult to follow with those large methods. I wouldn't keep track of the processing cells in an array. Each cell has an object to represent, those object have a bool value of ready and a progress value, right?. So try something like this:

  • Make sure each of your cells have a progressView as a subview.
  • Your cell class should have a public method named styleForReady:(bool)isReady andProgress:(nsinteger)progress
  • Make the service call to see if they are done or not, for each model. Whenever that service call comes back, you just update the model objects in the array, and after they have the new progress values you do [self.tableView reloadData]. This would trigger numberOfRows (which should return arrayOfObjects.count) and cellForRowAtIndexPath:(which should dequeue the cell for that indexPath, grab the model representing that cell, something like arrayOfObjects[indexPath.row], and finally, call the cell to style itself based on that model doing [cell styleForReady:objectModel.ready andProgress:objectModel.progress])

Keep in mind that the controller should just keep track of the model objects, dequeue the cell and tell the cell to style passing the model. Don't put all the logic in the controller.

Pauls
  • 2,596
  • 19
  • 19
  • thank you for response! didn't understand one moment: where should I get progress of each object now? in 'objectModel' class or in cell class? Do you mean that I must implement my `getProgress` method into subclass of `UITableViewCell`? – vendettacore Mar 27 '15 at 09:50
  • The controller is responsible for the model. Each model (a subclass of NSObject, should have progress and ready as properties). The cell is responsible for drawing, you should have a styleWithProgress: andReady: method in your cell, that will be responsible for updating the progressView based on the new progress and ready values from the model. When the call for the progress comes back, you update the models (not the cell), and then call reloadData, which will trigger all the UI methods required to paint the tableViewCells based on the new updated models. – Pauls Mar 27 '15 at 11:22
  • ok I implemented method `getProgress` into my model class (subclass of NSObject) and on each call of this method I change value of this class "readyProgress" property, also I implemented method `-(void)styleForReady:(BOOL)isReady andProgress:(float)progress` (i need float value for it.. doesn't matter. and now in my `cellForRowAtIndexPath` I check for objects which are not ready and then call `[cell styleForReady:NO andProgress:[oneItem getProgress]];` but I don't understand where should I call `[self.tableView reloadData];` thats why I can see changes only while scroll – vendettacore Mar 27 '15 at 11:46
  • You should not put state in the cell. State should be kept in the dataSource. – Hot Licks Mar 27 '15 at 12:14
  • @vendettacore you have to do [self.tableView reloadData] everytime you want any cell to update, that is, whenever you have new values for your models. I would put it in viewWillAppear and whenever you receive new data from the server. reloadData calls cellforRow and numberOfRows methods, so it will refresh all visible cells and the ones coming when scrolling. – Pauls Mar 27 '15 at 12:44