In objective-c, when a button is pressed, I load a processing animation while a file is uploaded using:
[self performSelectorInBackground:#selector(loadAnimation) withObject:self];
This works and a loadAnimation image displays.
How do I stop it once the file has uploaded? I have tried:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(loadAnimation) object:nil];
But this does not stop the animation.
The loadAnimation is:
- (void) loadAnimation
{
loadingPng.hidden=NO;
NSArray *imageArray = [[NSArray alloc] initWithObjects:[UIImage imageNamed:#"0.png"], [UIImage imageNamed:#"1.png"], [UIImage imageNamed:#"2.png"], [UIImage imageNamed:#"3.png"], [UIImage imageNamed:#"4.png"], [UIImage imageNamed:#"5.png"], nil];
loadingPng = [[UIImageView alloc] initWithFrame:CGRectMake(487, 500, 50, 50)];
[self.view addSubview:loadingPng];
loadingPng.animationImages = imageArray;
loadingPng.animationDuration = 1.5;
[loadingPng startAnimating];
}
Just to make things clear performSelectorInBackground creates another thread, which executes the method and dies (if not attached to any runloop).
If during this execution something on the screen appears and you want to hide it, just call a whatever routine needed to hide it. If you use UIActivityIndicatorView or UIImageView call -(void)stopAnimating (in this case [loadingPng stopAnimating]).
There is no need in starting animation in background, actually it's a thing that should not be done, because anything that involves UI manipulation is highly recommended to be done only on the main thread.
It's a loading that should go to background, and animation triggering stays on the main thread.
Related
I have an ACPDownloadView instance of AppDelegate. It's showing downloading progress. When I update it's progress value in my view controller, it works fine.
appdelegate.downloadVew=[[ACPDownloadView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
appdelegate.downloadVew.backgroundColor=[UIColor clearColor];
progressBtn= [[UIBarButtonItem alloc] initWithCustomView:appdelegate.downloadVew];
ACPIndeterminateGoogleLayer * layer = [ACPIndeterminateGoogleLayer new];
[layer updateColor:[UIColor blueColor]];
[appdelegate.downloadVew setIndeterminateLayer:layer];
[appdelegate.downloadVew setIndicatorStatus:ACPDownloadStatusRunning];
[appdelegate.downloadVew setProgress:0.0 animated:NO];
Now, I have a delegate method which called when progress updated.
-(void)progressAddChannel:(NSProgress *)uploadProgress{
float prog=uploadProgress.fractionCompleted;
[appdelegate.downloadVew setProgress:prog animated:YES];
[appdelegate.downloadVew layoutIfNeeded];
NSLog(#"%f",prog);
}
This code executes and show correct value of progress. But that progress is not animating on appdelegate.downloadVew. Do anyone know about this behavior?
Since you are making UI changes make sure your code runs on the main thread.
This is what I did:
#implementation BGUIActivityIndicator
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
[self customInitialize];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
[self customInitialize];
return self;
}
-(void)customInitialize
{
UIView * theImageView= [self findASubViewforClass:[UIImageView class]];//
UIImageView * theImageView1 = (UIImageView *) theImageView;
theImageView1.image = [UIImage imageNamed:#"spinner_blue"];
[theImageView1.image saveScreenshot];
while (false) ;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Everything seems perfect. [theImageView1.image saveScreenshot]; jot down both the old and new view perfectly.
However, nothing changes. Why?
I am not exactly asking how to change the image of UIActivityIndicator. There are tons of it already. I want to use it by subclassing UIActivityIndicator because I think it's the most elegant solution. I can't seem to do that.
In particular, I am asking why my approach, which works for changing background of search controller, for example, doesn't work for this.
According to the UIActivityIndicatorView Class Reference ,there is no way/ chance to change the image through sub-classing.
However you can change its activityIndicatorViewStyle , color of the activity indicator,UIActivityIndicatorStyle etc..
I think, without sub-classing, the class class UIImageView provides a very useful and simple way to implement such a thing. The only thing you have to do is to:
1.Provide a number of images that reflect your indicator animation.
2.Create a new UIImageView instance and set images and animation duration.
3.Position your custom activity indicator within your current view.
SAMPLE CODE:
//Create the first status image and the indicator view
UIImage *statusImage = [UIImage imageNamed:#"status1.png"];
UIImageView *activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"status1.png"],
[UIImage imageNamed:#"status2.png"],
[UIImage imageNamed:#"status3.png"],
[UIImage imageNamed:#"status4.png"],
[UIImage imageNamed:#"status5.png"],
[UIImage imageNamed:#"status6.png"],
[UIImage imageNamed:#"status7.png"],
[UIImage imageNamed:#"status8.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 0.8;
//Position the activity image view somewhere in
//the middle of your current view
activityImageView.frame = CGRectMake(
self.view.frame.size.width/2
-statusImage.size.width/2,
self.view.frame.size.height/2
-statusImage.size.height/2,
statusImage.size.width,
statusImage.size.height);
//Start the animation
[activityImageView startAnimating];
//Add your custom activity indicator to your current view
[self.view addSubview:activityImageView];
See the full details Here
Ok so i have a uitableview and when an item is selected it segues to a new view controller to show an image (inside of a uiscrollview). The image begins downloading in a dispatch_queue from prepareforsegue. To be clear I am doing all UI updates in the main queue. The problem is that if i hit the back button quick enough then my program crashes with an exc_bad_address. I think the problem has to deal with zoomToRect animated:YES because when i set animated to NO i cant get it to crash. Plus the call stack below deals with animation. What is the best way to go about fixing this and is there a better way to get what i need done?
Also when debugging the problem print 'Block completed' and then crashes shortly after.
stack trace
Here is the method called. It is called in a setter of the destination controller in prepareForSegue.
-(void) updateDisplay {
dispatch_queue_t queue = dispatch_queue_create("Load Flickr Photo", NULL);
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:spinner];
[spinner startAnimating];
dispatch_async(queue, ^{
UIImage *image = [FlickrFetcher imageForPhoto:self.currentPhoto format:FlickrPhotoFormatLarge];
dispatch_async(dispatch_get_main_queue(), ^{
self.navigationItem.rightBarButtonItem = nil;
self.imageView.image = image;
self.title = [FlickrFetcher titleForPhoto:self.currentPhoto];
CGAffineTransform transform = CGAffineTransformMakeScale(1.0, 1.0);
self.imageView.transform = transform;
self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height);
self.scrollView.maximumZoomScale = 4.0;
self.scrollView.minimumZoomScale = .2;
self.scrollView.zoomScale = 1;
self.scrollView.contentSize = self.imageView.bounds.size;
//i think problem is here
[self.scrollView zoomToRect:self.imageView.frame animated:YES];
NSLog(#"Block completed");
});
});
}
I think a potential problem could be that while your block retains your self the view controller while its active in queue...
If you send -zoomToRect:animated:NO, the block will still be active and blocking on the call which ensures that all the objects are still valid in memory.
If you send -zoomToRect:animated:YES, the block will exit potentially generating a race condition where your view controller would be released since storyboards will also release your view controller when you go back in a segue leaving it with an effective retain count of zero.
in my nib file I have a UIImageView that I have linked to my variable IBOutlet UIImageView* imgH6; I need to play several animations therefore I have decided to play them on imgH6. for example if I want to start animating a group of images I will do: [self playAnimation1];
-(void) playAnimation1{
NSArray *imagesArrayDilema;
imagesArrayDilema = [NSArray arrayWithObjects:
[UIImage imageNamed:#"dilema1-02.png"],
[UIImage imageNamed:#"dilema1-03.png"],
[UIImage imageNamed:#"dilema1-04.png"],
[UIImage imageNamed:#"dilema1-05.png"]
// .....
// there are several images
, nil];
imgH6.animationImages = imagesArrayDilema;
// I will then start the animation as:
wait = ((double)[imgH6.animationImages count])/30;
[imgH6 setAnimationRepeatCount:1];
imgH6.animationDuration = wait;
[imgH6 startAnimating];
}
-(void) playAnimation2{
NSArray *imagesArrayDilema;
imagesArrayDilema = [NSArray arrayWithObjects:
[UIImage imageNamed:#"differentImage-01.png"],
[UIImage imageNamed:#"differentImage-02.png"],
[UIImage imageNamed:#"differentImage-03.png"],
[UIImage imageNamed:#"differentImage-04.png"],
// .....
// there are several images
, nil];
imgH6.animationImages = imagesArrayDilema;
// I will then start the animation as:
wait = ((double)[imgH6.animationImages count])/30;
[imgH6 setAnimationRepeatCount:1];
imgH6.animationDuration = wait;
[imgH6 startAnimating];
}
///.....
// ...
-(void) playAnimation8{ // etc...
so when I call [self playAnimation1]; there is no problem. the animation animates fine. I may also call [self playAnimation7]; and a different group of images animate. I am able to do this several times but eventually the application crashes! In order to fix this I have done the following: I have a global BOOL variable called isAnimating and what that does is that every time there is an animation in place you will not be able to animate a different group of images until the animation is done. Before I used to have the method:
-(void) animateMain :(NSArray*) imagesArray{
imgH6.animationImages = imagesArray;
// I will then start the animation as:
double wait = ((double)[imgH6.animationImages count])/30;
[imgH6 setAnimationRepeatCount:1];
imgH6.animationDuration = wait;
[imgH6 startAnimating];
}
and the application still crashed after playing several times. I have noticed that it crashes when changing the animation several times. In other words if I alway splay the same animation it does not crash...
also I do not used the word alloc or init to instantiate the array so I don't think this has to do with releasing the array.
I have an application that hangs whenever I call NSPrintOperation.
I have a view that is creates a separate class (UIView) like this:
PBPrintImage *printImage = [[PBPrintImage alloc] init];
printImage.image = finalImage;
[printImage printWithNoPanel:self];
Then inside PBPrintImage I have the following method:
- (void)printWithNoPanel:(id)sender {
CGSize picSize = CGSizeMake(300, 446);
NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
NSRect imageRect = NSRectFromCGRect(CGRectMake(0, 0, picSize.width, picSize.height));
NSImageView *imageView = [[NSImageView alloc] initWithFrame:imageRect];
[imageView setImage:image];
NSPrintOperation *op = [NSPrintOperation printOperationWithView:imageView printInfo:printInfo];
[op setCanSpawnSeparateThread:YES];
[op setShowsPrintPanel:NO];
[op runOperation];
}
If I don't call it the application works as suspected. And I've tried calling it with and without setCanSpawnSeparateThread:. How do I set it up so it has to be in a separate thread and therefore doesn't mess up the regular flow of the application?
It does print, but that is only half of the job.
The application should show a modal print dialog (and start a modal run loop), so I would not call it "hanged". It returns to the normal main thread flow as soon as you hit Ok or Cancel.
As for the setCanSpawnSeparateThread: issue, it only kicks in when the print dialog is displayed as a sheet, so you need to call it like this: `[op runOperationModalForWindow:window delegate:self didRunSelector:#selector(_printOperationDidRun:success:contextInfo:) contextInfo:nil]