4

I have a PhotoViewController class with an @property UIActivityIndicatorView* spinner. FlickrPhotoViewController is a subclass of PhotoViewController that downloads a photo from Flickr and tells the spinner when to start and stop animating. 'updatePhoto' is called every time the view controller is given a Flickr photo:

- (void)updatePhoto { // Download photo and set it
    NSLog("updatePhoto called");
    if (self.spinner) NSLog(@"Spinner exists in updatePhoto");
    dispatch_queue_t downloadQueue = dispatch_queue_create("downloader",
                                                           NULL);
    [self.spinner startAnimating];

    dispatch_async(downloadQueue, ^{
        // Download the photo
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.spinner stopAnimating];
            // Set the photo in the UI
            }
        });
    });
}

The above methodology is exactly what I use for displaying a spinning wheel in my table view controllers while the table contents download, and it always works there.

You will notice at the beginning of updatePhoto I print a message if the UIActivityIndicatorView exists. I put a similar statement in awakeFromNib, viewDidLoad, and viewWillAppear. When I run it, this is the exact output I get:

2013-01-31 21:30:55.211 FlickrExplorer[1878:c07] updatePhoto called
2013-01-31 21:30:55.222 FlickrExplorer[1878:c07] Spinner exists in viewDidLoad
2013-01-31 21:30:55.223 FlickrExplorer[1878:c07] Spinner exists in viewWillAppear

Why does spinner not exist in awakeFromNib? Docs indicate that "When an object receives an awakeFromNib message, it is guaranteed to have all its outlet and action connections already established." Can an IBOutlet be connected without the existence of the object it is connecting to? In this case, can the spinner IBOutlet be connected to the storyboard without spinner being allocated?

Moving beyond this, I overrode the getter for spinner so that it would instantiate if it does not exist. As a result, the printing output now looks like this:

2013-01-31 21:48:45.646 FlickrExplorer[2222:c07] Spinner exists in awakeFromNib
2013-01-31 21:48:45.647 FlickrExplorer[2222:c07] updatePhoto called
2013-01-31 21:48:45.647 FlickrExplorer[2222:c07] Spinner exists in updatePhoto
2013-01-31 21:48:45.649 FlickrExplorer[2222:c07] Spinner exists in viewDidLoad
2013-01-31 21:48:45.650 FlickrExplorer[2222:c07] Spinner exists in viewWillAppear

This is what I would have expected to see earlier. Nevertheless, I still do not get any animation.

Possible problems that I have ruled out:

All three of these possibilities are ruled out by the fact that putting [self.spinner startAnimating]; in viewWillAppear makes it successfully animate throughout the download process.

You can download this project if you like. Just go to any screen that attempts to display a large photo and you will see that the spinner does not appear. There are many problems with this project, but this is the one I am focusing on now.

Edit 1:

  • I added the project's missing dependencies on Git, so the project will now compile for you

Edit 2 (2 February 2013):

  • I am seeing this problem only on the iPhone when the updatePhoto method is called due to another view controller's prepareForSegue setting the photo in the FlickrPhotoViewController. Is it possible that this contributes to the problem?
Community
  • 1
  • 1
Xander Dunn
  • 2,349
  • 2
  • 20
  • 32
  • 3
    why dont you just use MBProgressHUD https://github.com/jdg/MBProgressHUD and keep yourself from all that `ActivityIndicator` troubles – SpaceDust__ Jan 31 '13 at 17:24
  • It has been a long time since I've read such a good, concise question with this amount of research effort. +1. And accept a piece of advice from me: don't stress yourself using Interface 'crappy' Builder. Code works just fine. –  Jan 31 '13 at 17:26
  • +1 on the question completeness. Motivated me to get your code and look through it. Tough to test because it's nowhere near compiling. I do notice a few things: your view is white in IB, also, the code you reference in the question is getting called by the setPhoto: setter, which is called by an external VC, it's not at all clear that your FlickrPhotoViewController is properly initialized before this UI related stuff starts happening. – danh Jan 31 '13 at 17:34
  • Another good question completeness practice is to boil the problem down to the minimal situation that demonstrates the issue. e.g. Can you see if you can just get a spinner going on that VC whenever it appears? – danh Jan 31 '13 at 17:36
  • Did you add the spinner as a subview? Did you try commenting stopAnimating? Maybe it's called very quickly and doesn't give it a chance to animate. Can you give a sample project? – Yariv Nissim Jan 31 '13 at 21:04
  • @SpaceDust Thanks for the reference to this project. For any real project, I will probably use a library like that. However, I am currently a beginner and I would really like to figure out why this is happening. – Xander Dunn Feb 01 '13 at 04:21
  • @danh Thanks for your compliment. Sorry the project isn't building. I never make a commit unless a project is building, so I must not have some dependencies included in the project commits. Sorry. I'll fix this as soon as I get to my computer. Also, as indicated at the bottom of my question, I can indeed get the spinner going if I mess with viewWillAppear. – Xander Dunn Feb 01 '13 at 06:07
  • @yar1vn I'm not exactly sure how you mean to add it as a subview. I am using storyboards, so I would expect the storyboard to take care of that. Commenting `stopAnimating` is a good idea, but this does not seem to be the problem based on the fact that calling `startAnimating` from `viewWillAppear` works just fine, as indicated in my original post. Also, a link to the original project is included in my original post. – Xander Dunn Feb 01 '13 at 06:21
  • I cannot build this project. It's missing files.. – Yariv Nissim Feb 01 '13 at 06:27
  • @yar1vn Yeah, sorry, another commenter pointed this out. This is my fault. I haven't included all the dependencies in the project commits. I will fix this as soon as I get home. I happen to live in a 3rd world country, and I'm replying to this message via an Edge network on my lunch break. Provided we have electricity when I get home, I will be able to fix the GitHub commit. – Xander Dunn Feb 01 '13 at 06:43
  • @H2CO3 You mentioned that Interface Builder is crappy. Do you have any links to articles that explain why this is the case? It has worked well for me, and it seems to me a great way to document the UI and to remove a lot of useless initialization code. – Xander Dunn Feb 02 '13 at 01:12
  • @thoughtadvances Articles? Perhaps my articles in the future :) This is my personal opinion (i. e. I strongly dislike it, which doesn't by itself mean that others do as well). My reasoning: 1. I find its structure illogical and quite confusing, you can never know what is connected to what, which file belongs to which object, not even which designated initializer is called (when using your custom `UIView[Controller]` subclasses), etc. 2. If an app is developed using IB, there's no goddamn way a developer without IB can work on it. 3. It makes lazy beginners avoid learning to code a UI. –  Feb 02 '13 at 05:57

1 Answers1

1

Try to fire start animating uiactivityindictor before u call updatePoto method like this Tru calling this iinstead of calling UpdatePhoto just all :

[self startAnimatingAndThenUpdatePhoto];


-(void)startAnimatingAndThenUpdatePhoto{
 if (self.spinner) NSLog(@"Spinner exists in updatePhoto");
  [self.spinner startAnimating];
 [self performSelector:@selector(updatePhoto) withObject:nil afterDelay:0.01];
}
 - (void)updatePhoto { // Download photo and set it
        NSLog("updatePhoto called");
        dispatch_queue_t downloadQueue = dispatch_queue_create("downloader",
                                                               NULL);

        dispatch_async(downloadQueue, ^{
            // Download the photo
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.spinner stopAnimating];
                // Set the photo in the UI
                }
            });
        });
    }

Not the best method in the world but it will do thejob

Hmeedo
  • 94
  • 3
  • Ugh! This worked! BUT WHY!? Earlier I found a StackOverflow answer suggesting to do it the other way around: `- (void) updatePhoto { [self performSelector:@selector(startSpinning) withObject:nil afterDelay:0.01]; // Code to update the photo ` However, that did not work for me. Reversing which method is called via `performSelector` does work! But, this is a horrible, evil, ugly solution! Why is this necessary here and not other places? Why is there a hard coded 0.01 in my code!? – Xander Dunn Feb 02 '13 at 01:18
  • I made a big mistake. You can completely ignore that last comment of mine. I'm afraid this idea did not help the problem at all. I tested it on iPad after applying this fix. But, I have never had a problem with the spinner animation on iPad. Like before, it still doesn't work on iPhone. – Xander Dunn Feb 02 '13 at 06:35