i noticed some strange behavior. When i start a animation and change the View (the view will not dismissed!), the completion handler never get called.
[UIView animateWithDuration:0.1f
delay:0.0f
options:UIViewAnimationCurveEaseOut
animations:^(void){
[myView setHidden:YES];
myLabel.alpha = 0.0f;
someOtherView.frame = CGRectMake(130, bubbleBigRect.origin.y, 61, 65);
[button setHidden:YES];
}
completion:^(BOOL finished){
NSLog(#"Complete %d",finished);
[imageVIew setImage:[UIImage imageNamed:#"myPng.png"]];
}];
}
is there any solution for this?
Don't know where to write it, but I get the same thing that you have, but in my case the completion block was sometimes called. It might be same thing.
I found out that if in the animation block has nothing was animate- for example, if you set alpha=0 to uiview that is alpha is already 0, or you set content offset to UIScrollView to the same content offset (like in my case), the completion block not called.
Put this in your animation block, and put everything you want to do on completion in YOUR_SELECTOR method. You can now do whatever you want with your view and still have a way of executing something on completion!
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(YOUR_SELECTOR)];
In my case the view which I was animating has its frame set to (0,-568,320,568) and after animation I forget to change the frame to its required position i.e. (0,0,320,568) so the completion block was not being called. Changing the frame of the animated view did the job for me. So I can say if for instance the view has nothing to show(as the frame was not set in visible region). The completion block may not called.
Move your call to [myView setHidden:YES] from the animations block to the completion block. I think that will probably help. You can still set the alpha of myView to 0 during the animation block (if you want it to fade out), or before the entire call to -[UIView animateWithDuration:delay:options:animations:completion:] if you want it to disappear right away.
Related
I'm wandering what is the correct way to do "long" actions in response to user events. For example, I have this slide to cancel block that animates itself off screen over 0.5 seconds. The [self coreDataAction] may take about 0.3 seconds itself.
I want to ensure that the action completes once the user sees the end of the animation ( I do not want the user to accidentally navigate to a different controller or close the app thinking that the action is done).
Where should I put the [self coreDataAction]; in this case? Above the block, within the block or in the completion block?
//should I put it here?
CGPoint slideToCancelCenter = slideToCancel.view.center;
[UIView animateWithDuration:0.5 animations:^{
self.goToSleepButton.center = slideToCancelCenter;
[UIView setAnimationDuration:0.5];
CGPoint sliderCenter = slideToCancel.view.center;
sliderCenter.y += slideToCancel.view.bounds.size.height;
slideToCancel.view.center = sliderCenter;
//should I put it here?
// [self coreDataAction];
} completion:^(BOOL finished) {
//should I put it here?
} ];
A better way to handle this might be to animate the view on-screen then start the coreDataAction in the completion handler. Once the coreDataAction method execution is complete you can call a method to animate the slide to cancel view off-screen.
Assuming [self coreDataAction] executes on the main thread, I would say you should put it on the first line to ensure that the method is complete by the time the animation is done.
I wrote the following code :
Code:
[MyImage setHighlighted:true];
Sleep(1);
[MyImage setHighlighted:false];
i need to highlight my image for one second and after that get it back , but X-Code when arrive to [MyImage setHighlighted:true] do not any thing ( i know it do it ) and after that Sleep executed and main thread of application goes to sleep for 1 second and after that the last line executed , but my question is why the first line do not update the UI and i can,t see any thing in user interface .
In first line of code the Sleep method do not executed and main thread is in normal state and it should set my image to highlight and after that goes to sleep for 1 second and after that set my image to normal state , but why not ?
I know that i can use NSTimer to do it but why the following code do not work ?
Thanks .
jackslash explains well what is happening.
You can change your code into something like this:
[image setHighlighted:true];
[self performSelector:#selector(aMethodThatChangesTheHighlight) withObject:self afterDelay:1];
Finally note that freezing the main thread is generally a bad idea.
changes to the screen are only made once your method returns. Changes are then all coalesced and made at once. In this case as you are sleeping the main thread your method does not return until you un highlight the image. After your method returns UIKit looks to see if it needs to draw any changes. As you have undone your changes before your method returned there is nothing to do so no change happens on the display.
The best thing to do would be to see if you can use the UIView class animation methods.
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^{
//animation code goes here
//note that you have to change something that UIView considers to be animatable
//e.g.
self.myView.alpha = 0;
} completion:^(BOOL done){
//code to execute once the animation has completed goes here
}];
I like sch's answer better, but if you wanted to use animation as jackslash suggested, you would have to make the animation change something visual so that the completion block does not run immediately. Not even adding a "delay" will fix that.
MyImage.alpha = 0.99f;
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{
MyImage.alpha = 1.0f;
[MyImage setHighlighted:YES];
} completion:^(BOOL finished) {
[MyImage setHighlighted:NO];
}];
I just upgraded my iPhone 4 from iOS 4.2.1 to 4.3.2, and to XCode 4.0.2, and I am encountering some bizarre issues with uiview animations. When I first launch my app, code like this executes perfectly:
[UIView beginAnimations:#"fadeAlphaIn" context:nil];
[UIView setAnimationDuration:0.5f];
viewClue.alpha = 1.0f;
[UIView commitAnimations];
But then, after dismissing a presenting and then dismissing a modal view by the standard method:
[self presentModalViewController:more animated:YES];
and
[self dismissModalViewControllerAnimated:YES];
the first animation no longer works. Instead of fading in, for example, the viewClue view simply jumps from alpha = 0 to alpha = 1. Similarly, other animations altering other views' frame property just force the frame to jump from the initial to final value without animation. These animations worked fine before the modal view was presented and dismissed.
I understand that others have experienced animation issues with the upgrade to iOS 4.3.2, but the way the modal view disrupts animation seems very odd. Has anyone else experienced this problem? Any ideas as to a solution? I'm thinking of just adding the modal view as a subview and animation it as it hides and appears, but using the standard modal view method would be much preferred.
Thanks for your help,
James
EDIT: Some more code showing how the app's map is animated
-(void) viewMapfunc
{
AudioServicesPlaySystemSound(soundID);
if(mapvisible){
[UIView animateWithDuration:0.5
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
map.frame = CGRectMake(0, 350, 320, 27);
mapscroll.frame = CGRectMake(0, 27, 320, 0);
}
completion:nil];
mapvisible = NO;
viewMapLabel.text = #"View Map";
}else {
[UIView animateWithDuration:0.5
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
map.frame = CGRectMake(0, 50, 320, 300);
mapscroll.frame = CGRectMake(0, 27, 320, 300);
}
completion:nil];
mapvisible = YES;
viewMapLabel.text = #"Hide Map";
}
}
Try to check two things:
Do you commit all started animations? I got all kinds of strange effects after not committing one of them.
Do any animations take place in the same time? Especially with the same view.
Whether any animations take place right after changing properties. Something like:
-
view.alpha = 1;
[UIView beginAnimations:…];
view.alpha = 0;
[UIView commitAnimations:…];
In this example, view will not change it's alpha value from 1 to 0. It will change it instantly. To start an animation you have to extract animations block to another method and call it with performSelectorInMainThread:withObject:afterDelay:. Delay can be even 0.
I solved it by restarting my animation in my UIView subclass:
override func willMove(toWindow newWindow: UIWindow?) {
if newWindow != nil {
spinner.startSpinning() // Restart any animation here
}
}
In the end, I just removed all modal views and implemented them in other ways. For some reason, using modal views messed up animations. Makes no sense, but removing them fixed the problem. If anyone can enlighten me as to why this is going on, it might be nice for memory concerns...
I had the same issue. The root of my trouble was that my animation was being triggered by a notification, and I was adding an observer on each viewWillAppear, but forgot to remove in viewDidDisappear (remember that iOS 6 no longer calls viewDidUnload reliably).
Essentially, I was calling my animation function twice in quick succession, which was causing the visible irregularity. Hopefully this helps someone out down the line!
I've managed to solve this same issue in my own application.
I noticed while debugging that my UIImageViews which I was animating had different memory addresses before and after I pushed my modal view controller(s). At no other time did these UIImageViews switch their memory addresses.
I thought this might have been the root of the issue and it seems I was right.
My client's code had been allocating/initializing my View Controller's UIImageViews in
-viewDidAppear instead of in -viewDidLoad. Thus, every time I launched and dismissed a modal view controller my UIImageViews I was animating would get reinitialized.
Check for yourself if your map object's memory address is changing before and after you launch your modals, and if it is be sure to move your initialization logic to a more proper section of your code.
Hope this helps you!
Dexter
I was using UIView animateWithDuration: and I solved it by not using the completion block. This is code from a subclassed UIView. In the view controller's viewWillAppear: I set self.shouldAnimate to YES, and in the view controller's viewWillDisappear: I set self.shouldAnimate to NO.
-(void)continueRotate {
if (self.shouldAnimate) {
[self rotateRadarView:self.radarInner];
}
}
-(void)rotateRadarView:(UIView *)view {
[UIView animateWithDuration:0.6 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(continueRotate)];
[view setTransform:CGAffineTransformRotate(view.transform, M_PI_2)];
}completion:nil];
}
I am trying to dismiss a UIView that I have previously added using the same animation block with a fade from alpha 0 -> alpha 100 and [self.view addSubview:newInitiateWindow.view]; The animation block executes correctly when the window is created, but when it is being dismissed, it just halts for 0.75 seconds and then disappears without ever animating. Even if I remove the removeFromSuperview and release calls it doesn't animate the fade to transparent.
Here is the code that should be generating the animation:
[UIView transitionWithView:newInitiateWindow.view
duration:0.75
options:UIViewAnimationOptionCurveEaseIn
animations:^{
newInitiateWindow.view.alpha = 0;
}
completion:^(BOOL finished){
if(finished){
[newInitiateWindow.view removeFromSuperview];
[newInitiateWindow release];
newInitiateWindow=nil;
}
}
];
Were you literal with the 100? It's 1.0 for full alpha, maybe that's causing some issues.
It could be an intricacy with iOS and placement of the code, but it's likely something just being mistyped, could be something indirect with a spelling error
Transitions are supposed to add/remove subviews of the view listed in the transition. They're not supposed to modify properties of the view. You want +animateWithDuration:delay:options:animation:completion: instead.
I have connected the two methods below to separate buttons in my UI but have noticed that after pressing the "VERSION 1" button that I could not press the button again until the animation duration within the method had ended. My understanding was that the animation uses its own thread so as not to block the main application.
// VERSION 1
-(IBAction)fadeUsingBlock {
NSLog(#"V1: Clicked ...");
[myLabel setAlpha:1.0];
[UIView animateWithDuration:1.5 animations:^{
[myLabel setAlpha:0.0];
}];
}
The older style version (below) does allow the button to be repressed before the animation timer ends, simply resetting the timer to start again. Should these both work the same, am I missing something or has there been a change in operation between 3.2 and 4?
// VERSION 2
-(IBAction)fadeUsingOld {
NSLog(#"V2: Clicked ...");
[myLabel setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.5];
[myLabel setAlpha:0.0];
[UIView commitAnimations];
}
Cheers gary
Animating with blocks doesn't block the main thread. I think the behavior you're seeing is because, by default, user interaction is disabled duration animation with the new block calls. You can override this by passing UIViewAnimationOptionAllowUserInteraction (calling animationWithDuration:delay:options:animations:completion), like this:
-(IBAction) fadeUsingBlock {
NSLog(#"V1: Clicked ...");
[myLabel setAlpha:1.0];
[UIView animateWithDuration:1.5
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
[myLabel setAlpha:0.0];
}
completion:nil];
}
For animateWithDuration:, the class reference doesn't say anything about threading, so I am not sure.
For beginAnimations:context: and commitAnimation:, yeah, they run in a separate thread
UIView class Reference.
Some of the property changes to view objects can be animated—for example, setting the frame, bounds, center, and transform properties. If you change these properties in an animation block, the changes from the current state to the new state are animated. Invoke the beginAnimations:context: class method to begin an animation block, set the properties you want animated, and then invoke the commitAnimations class method to end an animation block. The animations are run in a separate thread and begin when the application returns to the run loop. Other animation class methods allow you to control the start time, duration, delay, and curve of the animations within the block.