I have a video player that has a standard toolbar. The toolbar is dismissed by a swipe down gesture. I also have a view (a panel really) that can appear directly above the toolbar and is also dismissed by a swipe down gesture. When both the panel and the toolbar are open, one swipe down gesture should dismiss the panel, a second will dismiss the toolbar. Problem is that when the swipe gestures occur quickly back-to-back (before the panel animation completes) then the toolbar animation jitters.
- (void)handleSwipe:(UISwipeGestureRecognizer *)gestureRecognizer
{
UISwipeGestureRecognizerDirection direction = [gestureRecognizer direction];
if (direction == UISwipeGestureRecognizerDirectionDown) {
if (![toolbar isHidden]) {
// Only dismiss the bottom panel if it is open
if (_selectedSegmentIndex != UISegmentedControlNoSegment) {
_selectedSegmentIndex = UISegmentedControlNoSegment;
[bottomPanelView dismissPanel];
} else {
CGRect tempRect = CGRectMake(0, self.view.frame.size.height, toolbar.frame.size.width, toolbar.frame.size.height);
[UIView animateWithDuration:0.25f
animations:^{
// Move the toolbar off the screen.
toolbar.frame = tempRect;
}
completion:^(BOOL finished) {
[toolbar setHidden:YES];
}];
}
}
}
}
[bottomPanelView dismissPanel] is in a separate class and is not aware of the class that calls it. It has the follow animation...
[UIView animateWithDuration:self.panelAnimationDuration
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
// slideOutLocation is off the screen
self.view.frame = slideOutLocation;
}
completion:^(BOOL finished) {
[self.view removeFromSuperview];
[self removeFromParentViewController];
self.panelActive = NO;
}];
So basically, the dismissPanel animation is still running when the animation to dismiss the toolbar begins. When performing a double swipe in slow motion in the simulator, the first animation looks fine, but the toolbar animation is jittery.
I know how to nest animations in the completion block, but that cannot be done here since dismissing both the panel and the toolbar is not always what is wanted. Also, the dismissPanel code is handled elsewhere and is not in control of the toolbar.
Is there a way to allow multiple animation blocks to run simultaneously without putting the completion block? Let me know if any clarification is needed! Thanks!
I wonder if the problem might have to do with auto layout (setting frames while auto layout is on causes problems). I tried a simple test of animating a view and a tool bar off the bottom of the screen by animating their constraint constants, and the animation looked fine. I made IBOutlets to their respective bottom constraints (called viewBottomCon and toolBarBottomCon).
- (void)viewDidLoad {
[super viewDidLoad];
self.isFirstSwipe = YES;
}
-(IBAction)downSwipe:(UISwipeGestureRecognizer *)sender {
if (self.isFirstSwipe) {
self.viewBottomCon.constant = -52;
self.isFirstSwipe = NO;
[UIView animateWithDuration:5 animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}else if (!self.isFirstSwipe) {
self.toolBarBottomCon.constant = -44;
[UIView animateWithDuration:3 animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
}
This is a simpler setup than yours, but I think it should work in your case too.
Related
I'm trying to maintain existing code and added a new UITextView to a View Controller. All of the views enter the screen by sliding in from the left side. My newly added view also slides in from the left side along with the other views (without me doing anything because the animation is set on their container). The problem is that my view also appears for a second or so before the animation starts. How can I prevent this from happening?
Here is the code for the animation on the container
- (void) showAndAnimateContainerView{
[self.containerView setHidden:NO];
[self hideContainerView:self.containerView];
self.marginLeft.constant = 0;
[UIView animateWithDuration:STD_ANIMATION_TIME delay:ZERO_ANIMATION_TIME options:UIViewAnimationOptionCurveLinear animations:^{
[self.containerView layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
- (void) hideContainerView:(UIView *) view{
self.widthView.constant = WIDTH_SCREEN;
self.marginLeft.constant = -WIDTH_SCREEN;
[view layoutIfNeeded];
}
Don't know what else I can add to the question to make it more clear.
I have a UIView called sineWave that renders an animated sine wave. I add sineWave as a subview by doing [self.view addSubview:self.sineWave];. I have another UIView called panedView which is the highest/top view/layer. As panedView is dragged up, sineWave view is revealed. I add panedView above sineWave view by doing [self.view insertSubview:panedView aboveSubview:self.sineWave];. When I pan up everything works perfectly. However, since adding the sineWave view I have an issue swiping up. When I swipe up, I can see that the swipe method gets called, but my view doesn't move up where it should. However, if I swipe a second time immediately after my first swipe, my view will move to where its programmed to - revealing the sine wave. Any idea why this would occur?
- (IBAction)handleUpSwipe:(UISwipeGestureRecognizer *)recognizer
{
NSLog(#"Swipe UP");
[UIView animateWithDuration:.1 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
panedView.frame = CGRectOffset(panedView.frame, 0, -1*self.view.bounds.size.height*.80);
} completion:nil];
[self displaySine];
}
-(void) displaySine
{
if(sineIsDisplaying == NO)
{
self.sineWave = [[OSDrawWave alloc] initWithFrame:CGRectMake(-1*self.view.bounds.size.width, (.75*self.view.bounds.size.height), 2*self.view.bounds.size.width, .3125*(2*self.view.bounds.size.width))];
[self.sineWave setBackgroundColor:[UIColor clearColor]];
[self.sineWave animateWave];
[self.view addSubview:self.sineWave];
[self.view insertSubview:panedView aboveSubview:self.sineWave];
sineIsDisplaying = YES;
}
}
I can't figure out what's happening here. I have two views on screen that serve as a "gate" until the camera is ready to record. Afterwards they slide out. It works, i'm happy, and that's great. The problem is regardless of when I choose to go "back" on the navigation controllers back option (be that during the animation or after it finishes) the left view "sticks out" on the view controller i'm going back to.
I've tried removing all animations -and- removing the views from the superview when the ViewWillDisappear method... but no luck, this view persistently sticks out on the home page when I click back from the recording page.
-(void)animateViewsOut
{
[UIView animateKeyframesWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionCurveEaseIn animations:^{
_leftView.layer.transform = CATransform3DMakeTranslation(-_leftView.frame.size.width, 0.0, 0.0);
_rightView.layer.transform = CATransform3DMakeTranslation(_rightView.frame.size.width, 0.0, 0.0);
} completion:^(BOOL finished) {
[_leftView removeFromSuperview];
[_rightView removeFromSuperview];
}];
}
and my attempt to solve the problem
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[_leftView.layer removeAllAnimations];
[_rightView.layer removeAllAnimations];
[_leftView removeFromSuperview];
[_rightView removeFromSuperview];
[[CameraEngine engine] shutdown];
}
So, as it turns out the problem's root was due to me inserting the views on the view controllers main view
[self.view addsubview:_leftView];
[self.view addsubview:_rightView];
I instead created a container for the views that I built onto the storyboard, then added the the views as subviews of that overview, and kept the same animation, except on completion instead of removing the views individually, I just remove the overlay container instead (change in the code below).
-(void)animateViewsOut
{
[UIView animateKeyframesWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionCurveEaseIn animations:^{
_leftView.layer.transform = CATransform3DMakeTranslation(-_leftView.frame.size.width, 0.0, 0.0);
_rightView.layer.transform = CATransform3DMakeTranslation(_rightView.frame.size.width, 0.0, 0.0);
} completion:^(BOOL finished) {
[_overlayView removeFromSuperview];
}];
}
After I made this change, the views didn't persist when tapping back in the navigation controller.
I removed all of the code from the viewWillDisappear method, removing the overlay container after the animation completed solved the problem on its own.
- (void) viewWillDisappear:(BOOL)animated {}
I created some animations in my project. Basically, I use UIView animate and CGAffineTransform, but a very strange thing happened and I have no idea. Hope someone can help me solve this problem. Thanks in advance.
This is the strange thing:
After the user clicks on a button, the button slides off screen and another two buttons slide on the screen (I just changed the center point of these buttons to achieve this animation). And, some time later, a view on the screen start shaking (I use CGAffineTransform to achieve this).
At this moment, the strange thing happens - the button that previous slid off screen show up at its original position again and the other two buttons disappear (No animation, just shows up and disappear).
The following is the related code,
1) Button slide off and slide in animation related code
- (IBAction)start:(id)sender
{
// 1. Slide in cancel and pause button
[UIView animateWithDuration:0.5f animations:^{
[startButton setCenter:CGPointMake(startButton.center.x + 300.0f, startButton.center.y)];
[cancelButton setCenter:CGPointMake(cancelButton.center.x + 300.0f, cancelButton.center.y)];
[pauseButton setCenter:CGPointMake(pauseButton.center.x + 300.0f, pauseButton.center.y)];
} completion:^(BOOL finished) {
if (finished) {
NSLog(#"Move finished");
}
}];
}
2) The view shaking animation related code
- (void)shakeView:(UIView *)viewToShake
{
CGFloat t = 2.0;
CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, t, 0.0);
CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, 0.0);
viewToShake.transform = translateLeft;
[UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
[UIView setAnimationRepeatCount:2.0];
viewToShake.transform = translateRight;
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
viewToShake.transform = CGAffineTransformIdentity;
} completion:nil];
}
}];
}
This isn't weird, it's a common problem with
auto layout. If you move UI elements by changing frames, then as soon as something else takes place that requires laying out views, the moved views will revert to the position defined by their constraints. To fix it, you either need to turn off auto layout, or do your animations by changing constraints not frames.
I have tried your code in my test project. The problem is probably because you are using Autolayout in your xib.
Please checking your xib file and uncheck the Autolayout property.
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];
}];