iOS 7 animation block not being called - ios

I have the following code to animation some view in my app:
void (^animate)() = ^() {
CGRect leftFrame = centerFrame;
leftFrame.origin.x -= centerFrame.size.width;
newViewController.view.frame = centerFrame;
oldViewController.view.frame = leftFrame;
};
if (animated) {
[UIView animateWithDuration:0.3f
delay:0.0f
options:nil
animations:animate
completion:^(BOOL finished){}];
} else {
animate();
}
This animates correctly on iOS 6, however on iOS 7 there is no animation. Oddly the code inside the block does get called and the view does update, just without the animation duration taken into account.
Is there a reason why this block isn't getting called?

I had the same issue: an animation block that works like a charm in iOS6 is executed without the animation in iOS7.
Not sure if this might help you but I moved the animation trigger to viewDidAppear: and now its being animated.
So for you this might look like this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
animate();
// do your other stuff here...
}

Your animation may be colliding with another competing animation block, perhaps one called by UIKit during a transition. Instead of passing nil to the animation options, use:
UIViewAnimationOptionOverrideInheritedOptions | UIViewAnimationOptionBeginFromCurrentState
and see if this helps.

I had exactly the same regression between iOS6 and iOS7.
In my case, it was due to the fact that animateWithDuration: was called before the UIView it animates appeared on screen.
In iOS7 it appears that you shouldn't call animateWithDuration: before -(void)viewDidAppear:(BOOL)animated is called on the controlling UIViewController.

I was having the same issue. What it turned out for me is I needed to reference a different part of the UITableViewCell I was animating than I did with iOS 6. It might be a similar issue for you.

It is probably autolayout in iOS 7 which is giving you troubles. I just had the same problem. Instead of animating the frame, you need to animate the constraints.
See Are NSLayoutConstraints animatable? for how to do it.

try using :
[UIView setAnimationsEnabled:YES];

Related

Block-Based Animation does not Work: Animations and Completion Blocks are Executed Immediately (Without Duration)

I tried to animate some features of the UIView, anyway, I simplified everything to these code lines in "ViewController.m":
- (void)viewDidLoad {
...
[UIView animateWithDuration: 5.0 animations:^{
NSLog(#"animations");}
completion:^(BOOL finished){NSLog(#"completion");}];
}
Both blocks outputs immediately after loading despite the fact that animation should last five seconds.
You can't use block animations in viewDidLoad because the UIView you want to animate doesn't have superview at the present moment. Move the code to the viewDidAppear method.

UIView animateWithDuration not animating in ios8

I have implemented a custom segue class for emulating the push transition without navigation bar. The trick is to take to snapshots, add them to the view, replace the viewController, move the snapshots, and finally remove them. This emules a horizontal movement of the viewController, but actually only two UIImagesView are moved.
The following code implements this.
self.destinationImageView.frame = offsetFrameD;
self.sourceImageView.frame = offsetFrameS;
//ViewController replacement
[self.sourceViewController presentModalViewController:self.destinationViewController animated:NO];
//Overpose the snapShot who will simulate the transition between vies.
[destinationView addSubview: self.sourceImageView];
[destinationView addSubview: self.destinationImageView];
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.destinationImageView.frame = self.finalFrameD;
self.sourceImageView.frame = self.finalFrameS;
}
completion:^(BOOL finished) {
[self.sourceImageView removeFromSuperview];
[self.destinationImageView removeFromSuperview];
}];
This code worked with iOS 7. However, when using iOS 8, it seems like the animation is not performed. The destinationImageView and sourceImageView are directly moved to the finalPosition (WITHOUT ANIMATING) and the completion block is not called, so both UIImageView are not finally removed.
Does anyone knows how the animation should be performed with iOS 8?
You should adjust the auto layout constraints and not the frames position. Have a look at this solution. I hope it helps.
UIView transitionWithView & setting the frame no longer works iOS8
Use the following line of code before the start of the animation:
[UIView setAnimationsEnabled:YES];

Fading in a background on opening a view

Seems simple, but I can't get this to work with all the different methods I try.
All I want to do, is fade in an image from a black background when I open a new view controller.
So far I've tried this:
//I've set the image alpha to 0 on the storyboard already
- (void)viewDidLoad
{
[UIView animateWithDuration:5.0
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
myImage.alpha = 1;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}
It runs, and the image comes up immediately (just doesn't seem to be animating!)
I know this is a simple question but I have already spent hours looking for a solution.
If anyone could help it would be much appreciated!
Thanks!
Animation should work from within -viewDidLoad but you may be doing something more in -viewDidLoad that is disrupting the animation.
or...
You could instead move your animation code to -viewDidAppear:
-(void)viewDidAppear:(BOOL)animated
{
//do the animation here
}

How to properly animate UIScrollView contentOffset

I have UIScrollView subclass. Its content is reusable - about 4 or 5 views are used to display hundreds of elements (while scrolling hidden objects reused and jumps to another position when its needed to see them)
What i need: ability to automatically scroll my scroll view to any position. For example my scroll view displays 4th, 5th and 6th element and when I tap some button it needs to scroll to 30th element. In other words I need standard behaviour of UIScrollView.
This works fine:
[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];
but I need some customisation. For example, change animation duration, add some code to perform on end of animation.
Obvious decision:
[UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
[self setContentOffset:CGPointMake(index*elementWidth, 0)];
} completion:^(BOOL finished) {
//some code
}];
but I have some actions connected to scroll event, and so now all of them are in animation block and it causes all subview's frames to animate too (thanks to few reusable elements all of them animates not how i want)
The question is: How can I make custom animation (in fact I need custom duration, actions on end and BeginFromCurrentState option) for content offset WITHOUT animating all the code, connected to scrollViewDidScroll event?
UPD:
Thanks to Andrew's answer(first part) I solved issue with animation inside scrollViewDidScroll:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
[UIView performWithoutAnimation:^{
[self refreshTiles];
}];
}
But scrollViewDidScroll must (for my purposes) executes every frame of animation like it was in case of
[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];
However, now it executes only once at start of animation.
How can I solve this?
Did you try the same approach, but with disabled animation in scrollViewDidScroll ?
On iOS 7, you could try wrapping your code in scrollViewDidScroll in
[UIView performWithoutAnimation:^{
//Your code here
}];
on previous iOS versions, you could try:
[CATransaction begin];
[CATransaction setDisableActions:YES];
//Your code here
[CATransaction commit];
Update:
Unfortunately that's where you hit the tough part of the whole thing. setContentOffset: calls the delegate just once, it's equivalent to setContentOffset:animated:NO, which again calls it just once.
setContentOffset:animated:YES calls the delegate as the animation changes the bounds of the scrollview and you want that, but you don't want the provided animation, so the only way around this that I can come up with is to gradually change the contentOffset of the scrollview, so that the animation system doesn't just jump to the final value, as is the case at the moment.
To do that you can look at keyframe animations, like so for iOS 7:
[UIView animateKeyframesWithDuration:duration delay:delay options:options animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
[self setContentOffset:CGPointMake(floorf(index/2) * elementWidth, 0)];
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
[self setContentOffset:CGPointMake(index*elementWidth, 0)];
}];
} completion:^(BOOL finished) {
//Completion Block
}];
This will get you two updates and of course you could use some math and a loop to add up a lot more of these with the appropriate timings.
On previous iOS versions, you'll have to drop to CoreAnimation for keyframe animations, but it's basically the same thing with a bit different syntax.
Method 2:
You can try polling the presentationLayer of the scrollview for any changes with a timer that you start at the beginning of the animation, since unfortunately the presentationLayer's properties aren't KVO observable. Or you can use needsDisplayForKey in a subclass of the layer to get notified when the bounds change, but that'll require some work to set up and it does cause redrawing, which might affect performance.
Method 3:
Would be to dissect exactly what happens to the scrollView when animated is YES try and intercept the animation that gets set on the scrollview and change its parameters, but since this would be the most hacky, breakable due to Apple's changes and trickiest method, I won't go into it.
A nice way to do this is with the AnimationEngine library. It's a very small library: six files, with three more if you want damped spring behavior.
Behind the scenes it uses a CADisplayLink to run your animation block once every frame. You get a clean block-based syntax that's easy to use, and a bunch of interpolation and easing functions that save you time.
To animate contentOffset:
startOffset = scrollView.contentOffset;
endOffset = ..
// Constant speed looks good...
const CGFloat kTimelineAnimationSpeed = 300;
CGFloat timelineAnimationDuration = fabs(deltaToDesiredX) / kTimelineAnimationSpeed;
[INTUAnimationEngine animateWithDuration:timelineAnimationDuration
delay:0
easing:INTULinear
animations:^(CGFloat progress) {
self.videoTimelineView.contentOffset =
INTUInterpolateCGPoint(startOffset, endOffset, progress);
}
completion:^(BOOL finished) {
autoscrollEnabled = YES;
}];
Try this:
UIView.animate(withDuration: 0.6, animations: {
self.view.collectionView.contentOffset = newOffset
self.view.layoutIfNeeded()
}, completion: nil)

How can I control UIView animation repeat cycle?

I'm afraid that the title is correct english expression but,
I have an animation on iOS with 4 scenes.
I used setAnimationDidStopSelector: method.
Here's the question: How can I make stopping function for the animation?
In iOS 4 and later you should use block based animation. Using these newer methods you can very easily specify some code to run once the animation has finished. For example:
[UIView animateWithDuration:1.0
animations:^{
// This code will be animated for 1 second.
[anObject setAlpha:0.0];
}
completion:^(BOOL finished) {
// This code will be executed once the animation has completed.
[anObject removeFromSuperview];
}];
A little ugly, but pretty lazy:
add a flag to know when it should animate, and when not. Put your animation under if block (or something same) and just switch flag when you need.

Resources