I want something similar in purpose to Flipboard slight flipping animation on app start. Flipboard when launched has this slight flipping of up and down to show users unfamiliar with the interface that it is flippable.
I have a UIScrollView I want to animate a bit to show the user that it's scrollable. So I want to scroll to the right a little bit and back. UIScrollView has a setContentOffset:animated: message without a completion clause. I find that calling it twice results in seemingly no animation. What if I want an animation after animation in succession?
EDIT:
Thanks Levi for the answer.
And for the record, there is UIViewAnimationOptionAutoreverse and UIViewAnimationOptionRepeat that I can use. So this is what I ended up with that works.
CGPoint offset = self.scrollView.contentOffset;
CGPoint newOffset = CGPointMake(offset.x+100, offset.y);
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAutoreverse |UIViewAnimationOptionRepeat animations:^{
[UIView setAnimationRepeatCount: 2];
[self.scrollView setContentOffset:newOffset animated: NO];
} completion:^(BOOL finished) {
[self.scrollView setContentOffset:offset animated:NO];
}];
For a scrollView, tableView or collectionView if you do something like this:
[self.collectionView setContentOffset:CGPointMake(self.collectionView.contentOffset.x+260.0,
self.collectionView.contentOffset.y)
animated:YES];
then you'll get back a:
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
when the scroll finishes.
You do NOT get this callback if the user moves the view.
Two options:
1) Use the -(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView delegate callback
2) Try to put it into an animation block (with ... animated:NO];), which has the completion part.
Related
I have a simple animation that im performing in my scroll view delegate method scrollViewDidEndDragging.
It looks like this:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
NSLog(#"finger was lifted");
[UIView animateWithDuration:1.0
animations:^{
self.homeLabel.frame = self.view.frame;
}];
}
Using this animation after lifting the finger the my homeLabel is coming from top, and i want to add it a bounce animation to the label, so when it comes from top, instead of landing smoothly it will have a nice bounce...how can i DO THAT? thanksss
You can use the usingSpringWithDamping animation function.
[UIView animateWithDuration:1.0 delay:0 usingSpringWithDamping:0.2 initialSpringVelocity:5.0 options:UIViewAnimationOptionCurveLinear animations:^{
self.homeLabel.frame = self.view.frame;
} completion:^(BOOL finished) {
}];
Adjusting the Spring Damping and Initial Spring Velocity can give you the effect you want.
One good solution is to create a custom layer for your view that overrides the addAnimation:forKey: method to include a custom timing function.
This answer goes into the specifics of how to do that.
Another option is to take a look at key frame animation. This question and answer covers that approach very well.
I want a scroll view to automatically scroll when the user first loads the screen... so I'm using this code:
[UIView animateWithDuration:(float)1.25f
animations:^{
myScrollView.contentOffset = CGPointMake(2000, 800);
}
completion:nil];
So the code works great and the UIScrollView simulates a "scroll" (animation) to the CGPoint I want, but while it's scrolling if the user wants to manually put their finger on it and start scrolling or just stop it, the user can't over-ride this animation until after it's completed.
Anyone have any ideas of a better method to animate it with over-ride capabilities, and when to call said over-ride capabilities (I'm assuming this will involve touchesBegan on scrollView's view)
Simply add UIViewAnimationOptionAllowUserInteraction to your animation options:
[UIView animateWithDuration:1.25f delay:0.0f options:(UIViewAnimationOptionAllowUserInteraction) animations:^{
_scrollView.contentOffset = CGPointMake(0, 800);
}
completion:nil];
And cancel the animation on the UIScrollView scrollViewWillBeginDragging: delegate method
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//Storing current offset
CALayer *currentLayer = _scrollView.layer.presentationLayer;
CGPoint scrollBoundsOrigin = currentLayer.bounds.origin;
//Cancelling animations
[_scrollView.layer removeAllAnimations];
//Restore offset
_scrollView.contentOffset = scrollBoundsOrigin;
}
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)
I have UIScrollView which can zoom an UIView. One time I want to zoom out to the default state.
Here is my code.
[UIView animateWithDuration:0.3
animations:^{
[scroll zoomToRect:self.view.bounds animated:NO];
} completion:nil];
Wow. It jumps like hell. Seems like it sets zoomScale = 1 and then animating the frame.
I'll add a line.
[UIView animateWithDuration:0.3
delay:0 options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[scroll zoomToRect:self.view.bounds animated:NO];
} completion:nil];
Works just fine. So how UIViewAnimationOptionBeginFromCurrentState helps in this case? From help I know
Start the animation from the current setting associated with an already in-flight animation.
But there is NO in-flight animations.
I found answer, it looks expedient but work fine.
write animation code in other place use method like 'performSelector:'
then it recognize correct current state
I have a horizontally-scrolling paging UIScrollView in an iPad app, containing lots of pages. On the last page, I tap on a button on the screen to reset back to page 1. I would like to be able to cross-dissolve this transition, but it doesn't seem to work:
[UIView transitionWithView:self.view duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve|UIViewAnimationOptionAllowAnimatedContent animations:^{
pagingScrollView.contentOffset = CGPointZero;
} completion:^(BOOL finished) {
[self refreshPages];
}];
I read that adding UIViewAnimationOptionAllowAnimatedContent will allow all content to transition, but it doesn't work. Instead, the screen cross-dissolves to the background colour, and when the transition is complete, the first page just appears.
you cannot fade-out a UIView (the scroller) AND simultaneously fade-in the same view...
you could just using different UIViews...
what you can do is:
1) fadeOut the scroller in the current position (to the backGround)
2) while the scroller is invisible, move it to the right position (with no animation)
3) fadeIn the scroller from the backGround
something like:
// START FIRST PART OF ANIMATION
[UIView animateWithDuration:0.5 delay:0.0 options: options:UIViewAnimationOptionTransitionCrossDissolve|UIViewAnimationOptionAllowAnimatedContent animations:^{
pagingScrollView.alpha = 0;
} completion:^(BOOL finished) {
// FIRST PART ENDED
// MOVE SCROLLER (no animation)
pagingScrollView.contentOffset = CGPointZero;
// START SECOND PART OF ANIMATION
[UIView animateWithDuration:0.5 delay:0.0 options: options:UIViewAnimationOptionTransitionCrossDissolve|UIViewAnimationOptionAllowAnimatedContent animations:^{
// fadeIn - animated
pagingScrollView.alpha = 1;
} completion:^(BOOL finished) {
// ANIMATION ENDED
[self refreshPages];
}];
}];
NEW EDIT:
thanks to amadour, who taught me something with his comments,
i hope he could add an answer of his own, i would vote for him
anyway, to answer to jowie original question:
i got the right animation just moving the contentOffset setting out of the animation block,
and removing UIViewAnimationOptionAllowAnimatedContent (not really needed), and passing pagingScrollView as parameter for transitionWithView
this worked for me:
pagingScrollView.contentOffset = CGPointZero;
[UIView transitionWithView:pagingScrollView duration:3.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
// pagingScrollView.contentOffset = CGPointZero; // move up, outside of animation block
} completion:^(BOOL finished) {
NSLog(#"-->> END amimation");
[self refreshPages];
}];