33
votes

I have one table view and in that table view i have one UIActivityIndicator with one button in every cell . Now on click on that button i want to start ActivityIndicator animating, its starting. But the problem is when i scroll the table view it stops animating. Here is my code for cellForRowAtIndexPath

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"txDevicesListCellID"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"txDevicesListCellID"];
    }
    UIButton *buttonutton = (UIButton *)[cell viewWithTag:103];
    UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *) [cell viewWithTag:104];
    button.tag = indexPath.row;
    return cell;
}

and selector method for my button is

-(IBAction)recevierButtonClick:(id)sender{
    UIButton *button = (UIButton *)sender;
    NSInteger index = button.tag;
    NSIndexPath* indexpath = [NSIndexPath indexPathForRow:serialNumber inSection:0];
    UITableViewCell* cell = [self.myTableView cellForRowAtIndexPath:indexpath];
    activityIndicator = (UIActivityIndicatorView*)[cell viewWithTag:index];
    [activityIndicator startAnimating];
}
2
if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"txDevicesListCellID"]; UIButton *buttonutton = (UIButton *)[cell viewWithTag:103]; UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *) [cell viewWithTag:104]; } button.tag = indexPath.row; try this. because of reinitialization of activity indicator that is not animating.RJV Kumar

2 Answers

88
votes

I think I can shed some light into when and how the spinners stop spinning in cells. I've subclassed UIActivityIndicatorView with my own class overloading the startAnimating and stopAnimating functions and put breakpoints in them. I've made a simple cell with only a spinner on it of my class. I've set the spinner's Animating property to true in IB: enter image description here

Now here's what happens. First two screenshots have a stack trace that indicates the two actions (stopping animations and starting them) are happening one after the other, in the same private function _didMoveFromWindow:toWindow: enter image description here enter image description here

It seems to me this is happening in the cell creation flow, so first it's initialized with no animation and then the IB settings kick in and start the animation. Now here's the interesting part, when the spinner stops animating: enter image description here

So seems that the spinner keeps spinning when the cell is removed from the screen and is stopped when the cell is prepared to be shown on screen again (prepareForReuse) via the private function _removeAllAnimations, which seems to iterate recursively all child views. The problem is that for some reason the UIKit's private functions never re-enable the animations and the startAnimating never gets called. Actually IMO the disabling of animations is the real problem.

The solution that I propose, and it's not perfect but it's apparently what Apple expects of us, is subclassing the UITableViewCell for cells that contain spinners and re-enabling them in prepareForReuse:

class SpinnerCell: UITableViewCell {
    @IBOutlet weak var spinner: UIActivityIndicatorView?

    override func prepareForReuse() {
        super.prepareForReuse()
        if let spinner = self.spinner {
            spinner.startAnimating()
        }
    }
}

Or in Obj-C:

@interface SpinnerCell

@property (weak) IBOutlet UIActivityIndicatorView *spinner;

@end

@implementation SpinnerCell

- (void)prepareForReuse {
    [super prepareForReuse];
    [self.spinner startAnimating];
}

@end
4
votes

You will need to maintain which cell's activity indicator has been started as tableView when scrolled load new visible cell sequentially. As result your activity indicator gets stopped.

Use one array for activity indicator status which will class member object.

NSMutableArray *mutArrActIndStatus;

//Intialization
mutArrActIndStatus = [NSMutableArray array];

Now add object equal to tableView datasource. Note 1 means started and 0 mean stop. So intially all be in stopping state

for(int i=0; i<[yourTableViewArray count]; i++)
{
    [mutArrActIndStatus addObject:@"0"];
}

Now use this in your tableView :

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   //...............
   //................

   UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *) [cell viewWithTag:104];
   button.tag = indexPath.row;

   NSString *strActIndStatus = mutArrActIndStatus[indexPath.row];
   if([strActIndStatus isEqualToString:@"1"])
   {
      [activityIndicator startAnimating];
   }

   return cell;
}

Next make changes in button's method to replace status of activity indicator

-(IBAction)recevierButtonClick:(id)sender{

   //for find index here to replace status of activity indicator
   UIButton *btnSender = (UIButton *)sender;
   [mutArrActIndStatus replaceObjectAtIndex:btnSender.tag withObject:@"1"];
}