UIActivityIndicatorView not spinning in UICollectionViewCell
Asked Answered
O

5

5

I have a custom cell that is loaded at the bottom of my collection view. Its only job is to display an activity indicator view - which happens while the app is making a new work call.

So I added it to the cell like so:

BBLoaderCell *loaderCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"LoaderCell" forIndexPath:indexPath];


UIActivityIndicatorView * activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.hidesWhenStopped = YES;
activityIndicator.hidden = NO;
activityIndicator.center = loaderCell.imageView.center;
activityIndicator.tag = 10;

[loaderCell.imageView addSubview:activityIndicator];

    [activityIndicator startAnimating];
    return loaderCell;

This shows an activity indicator in the last cell of my view - however it does not spin. Any ideas?

Solution Found

As per comments - it was a threading issue. I also took the suggestion to move the code to the custom nib file for the cell.

here is the refactored code:

BBLoaderCell *loaderCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"LoaderCell" forIndexPath:indexPath];
    loaderCell.backgroundColor = [UIColor clearColor];

     if (self.loadingMore == NO){
        dispatch_async(dispatch_get_main_queue(), ^{
            activityIndicator.hidden = YES;
            [loaderCell.spinner stopAnimating];
        });
    }
     else if (self.loadingMore == YES){
    dispatch_async(dispatch_get_main_queue(), ^{
        activityIndicator.hidden = NO;
        [loaderCell.spinner startAnimating];
    });
    }

    return loaderCell;

This is working as I need it to.

Thanks guys!

Oshiro answered 18/3, 2014 at 8:30 Comment(5)
I think this might have something to do with threading, could you try something like the following: [activityIndicator performSelector:@selector(startAnimating:) withObject:nil afterDelay:1];, the animation should start a second after the cell has loaded.Jackboot
Just a small suggestion, if you are using a nib file for your custom cell, just add the activity indicator in the actual .xib instead of adding it in cellForRow, keeps the code much cleanerScreamer
Thanks gents. Both of your comments helped. I will vote both. @hless please make your comment an answer so I can mark it.Oshiro
Glad it helped, even though I'm not 100% satisfied with the answer. Wish I knew exactly what causes this..Jackboot
I think it has to do with executing UI updates on a background thread. Which according to Apple should not happen and causes issues like the one above?Oshiro
J
2

I think this might have something to do with threading (eg. the UI on main thread not being ready to animate the indicator). You could try something like the following:

[activityIndicator performSelector:@selector(startAnimating:) withObject:nil afterDelay:1];

This is just a possible suggestion though, you should try different variations and check which one forces the animation onto the main thread correctly and in a timely matter. Another variation:

dispatch_async(dispatch_get_main_queue(), ^{
    [activityIndicator startAnimating];
});
Jackboot answered 18/3, 2014 at 9:23 Comment(0)
E
4

You most likely call reloadData more than once in a very short time. Maybe already in the next frame. The activity indicator hides when stops by default. This is what happens:

  1. At first reloadData it shows the spinner
  2. A second reloadData reloads also the cell and the spinner hides itself
  3. Now the spinner is hidden
  4. You scroll around but the cell get's reused. The spinner is still hidden

You can set activityIndicator.hidesWhenStopped = false. The spinner should now be always visible. But it's not spinning.

Adding this will resume the spinning.

override func prepareForReuse() {
   activityIndicator.startAnimating()
}

Another issue might be the color. Maybe the spinner has the same color as the background. Try to set the color of the spinner like this:

activityIndicator.color = .red

You can check if the spinner is there by using the "Debug View Hierarchy" Button Debug View Hierarchie

It will show you the wireframe of the current visible screen Wireframe

Emergence answered 31/5, 2021 at 8:38 Comment(0)
J
2

I think this might have something to do with threading (eg. the UI on main thread not being ready to animate the indicator). You could try something like the following:

[activityIndicator performSelector:@selector(startAnimating:) withObject:nil afterDelay:1];

This is just a possible suggestion though, you should try different variations and check which one forces the animation onto the main thread correctly and in a timely matter. Another variation:

dispatch_async(dispatch_get_main_queue(), ^{
    [activityIndicator startAnimating];
});
Jackboot answered 18/3, 2014 at 9:23 Comment(0)
S
1

A neater solution would be to start the animation in the willDisplay delegate method:

public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
  (cell as? BBLoaderCell)?.activityIndicatorView.startAnimating()
}

Since on iOS 10 or later, prefetching is enabled by default, there could be just 1 cellForItemAt invocation even when the cell is scrolled in and out of the screen multiple times. And when the cell is scrolled out of the screen it seems that stopAnimating() is called (isAnimating also becomes false). So using an async delay inside cellForItemAt may fail after the cell enters the screen the second time. It is still true on iOS 11

Since willDisplay will be called whenever the cell enters the screen, we can re-start the animation there reliably

Staten answered 10/9, 2018 at 2:13 Comment(0)
H
1

In my case i was using an activityIndicator on a customCell for a tableView and I had the same problem, even after writting down activityIndicator.startAnimating() the loading was not moving.

After hours of trying i came out with a solution.

override func prepareForReuse() {
       activityIndicator.startAnimating()
}

Just use the method prepareForReuse() show when you reuse the customCell the activityIndicator is animating

Harlequinade answered 23/4, 2021 at 7:49 Comment(0)
P
0

If subclass your UIActivityIndicatorView and put a breakpoint in the stopAnimation method you can see that on cell dequeue a private method is called that removes all animations. Which causes the indicator to be hidden or just stopped (if hidesWhenStopped is set to false).

Pasteurization answered 10/5, 2023 at 16:31 Comment(1)
Thanks for pointing that out, I've corrected my answer.Pasteurization

© 2022 - 2024 — McMap. All rights reserved.