2

Here is my code:

-(IBAction)saveDownloadedImage
{
    NSLog(@"Test");    EXECUTED
    indicatorView.hidden = NO;  NOT EXECUTED
    [indicatorView startAnimating];  NOT EXECUTED
    [statusLabel setText:@"WHY?"];  NOT EXECUTED
    [currentPicture setImage:[imageView image]];   EXECUTED
    ImageFileManager *fileManager = [[ImageFileManager alloc] init]; EXECUTED
    [fileManager saveImageToDisk:currentPicture]; EXECUTED
    indicatorView.hidden = YES;
    [statusLabel setText:@"Image saved successfully."]; EXECUTED
    saveButton.enabled = NO; EXECUTED

}

The proccess of saving takes about 5 seconds. So it would be normal to see the indicator in the UI. But nothing happens! Any idea?

gsach
  • 5,715
  • 7
  • 27
  • 42
  • Have you made sure that all your outlets are properly connected to IB? – JiaYow Feb 22 '12 at 13:09
  • @George read the sulthan answer.you have several option of showing the Indicator 1)Follow The Sulthan ANswer. 2)Create Separate Thraed And Call Indicator on that Thraed. See My Answer – Kamar Shad Feb 22 '12 at 13:23

4 Answers4

7

Everything is executed. Your problem is that the saveImageToDisk call is synchronous and you are calling it from the UI thread. When you are blocking the UI thread, nothing is ever repainted. The indicator is shown but it cannot be drawn to the screen until the IBAction returns when it will have been hidden again.

You have to call the saving method asynchronously.
Blocking UI thread is never a good idea.

Edit: see the answer for the following question for the correct solution: asynchronous calls to database in ios

Edit2: One of the possible solutions (not tested)


-(IBAction)saveDownloadedImage {
    indicatorView.hidden = NO; //Note you can use hidesWhenStopped property for this
    [indicatorView startAnimating];
    [statusLabel setText:@"BECAUSE..."];
    [currentPicture setImage:[imageView image]];

    [NSThread detachNewThreadSelector:@selector(save) toTarget:self withObject:nil]
}

- (void)save {
    @autoreleasepool {
        ImageFileManager *fileManager = [[ImageFileManager alloc] init];
        [fileManager saveImageToDisk:currentPicture];

        [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
    }
}

- (void)updateUI {
    indicatorView.hidden = YES;
    [statusLabel setText:@"Image saved successfully."];
    saveButton.enabled = NO;
}

Community
  • 1
  • 1
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Yes Sulthan ,you are absolutely Right.Another solution for that we can showthe Indicator In separate Thraed as I have shown In below code. – Kamar Shad Feb 22 '12 at 13:21
  • @George Sachin: Sulthan is right, you should run long tasks asynchronously. This might get you started: http://www.switchonthecode.com/tutorials/loading-images-asynchronously-on-iphone-using-nsinvocationoperation – Rok Jarc Feb 22 '12 at 13:21
  • He could add things like `[indicatorView setNeedsDisplay];` (same for the UILabel) before calling `[fileManager saveToDisk...`. – ott-- Feb 22 '12 at 13:22
  • @ott - That wouldn't work. `setNeedsDisplay` does not repaint immediately. And the indicator must be redrawn periodically anyway. – Sulthan Feb 22 '12 at 13:25
  • Really really helpful answer. Thanks Sulthan – gsach Feb 22 '12 at 13:35
  • Just edited to make it completely correct. It's difficult to write code without testing. – Sulthan Feb 22 '12 at 13:38
  • I use ARC and removed the @autoreleasepool. May that cause any problem? – gsach Feb 22 '12 at 13:45
  • If you are using ARC, leave `@autoreleasepool` there. The documentation says `the method aSelector is responsible for setting up an autorelease pool for the newly detached thread`, so you should do it. – Sulthan Feb 22 '12 at 13:55
1

Are you sure that

1) indicatorView and statusLabel are not null, and

2) indicatorView and statusLabel are added as subviews to self.view?

Rayfleck
  • 12,116
  • 8
  • 48
  • 74
  • I suspect that the code is executed but the objects are null or not added to the view as well. Put a breakpoint in the code and see what's going on. – madmik3 Feb 22 '12 at 13:18
1

In My Perception your are starting the Activity Indicator main Thread.

Instead Showing the Indicator on main thread

you should call Indicator on seperate Thread as Below.

-(IBAction)saveDownloadedImage
{
NSLog(@"Test");    EXECUTED
commented below code
//indicatorView.hidden = NO;  NOT EXECUTED
// [indicatorView startAnimating];  NOT EXECUTED
//instead of main thread create new Thread as Below
[NSThread detachNewThreadSelector:@selector(showloader) toTarget:self withObject:nil];
[statusLabel setText:@"WHY?"];  NOT EXECUTED
[currentPicture setImage:[imageView image]];   EXECUTED
ImageFileManager *fileManager = [[ImageFileManager alloc] init]; EXECUTED
[fileManager saveImageToDisk:currentPicture]; EXECUTED

[statusLabel setText:@"Image saved successfully."]; EXECUTED
saveButton.enabled = NO;
[indicatorView stopAnimating:YES];
indicatorView.hidden = YES;
}


//Custome method For shoing the Indicator.
-(void)showloader{
//call below method  here indicatorView object created already.
[indicatorView startAnimating]
}

It'll definitely work
Kamar Shad
  • 6,089
  • 1
  • 29
  • 56
  • It won't. The problem is the blocking call in UI thread, not how the indicator is shown. Your `showLoader` can be actually called when the saving has already completed! – Sulthan Feb 22 '12 at 13:24
0

You need to declare your method with a sender like this

-(IBAction)saveDownloadedImage:(id)sender
TheAmateurProgrammer
  • 9,252
  • 8
  • 52
  • 71
  • You should, but it won't stop it from executing. – Sulthan Feb 22 '12 at 13:10
  • 1
    Nothing wrong with his method signature, because there are 3 different correct signatures for an IBAction: http://stackoverflow.com/a/2117146/1207152 – sch Feb 22 '12 at 13:17