Stopping frame animation - ios

I am currently animating a view from left to right. However, I need to stop the animation mid-flight. To do this I use:
CGRect frame = [movingView layer] presentationLayer] frame];
[movingView.layer removeAllAnimations];
movingView.frame = frame;
Unfortunately the view appears to jump slightly to the left when this code is executed. How can I stop the animation in its exact place?

Related

change AVCaptureVideoPreviewLayer position instantly

I have an AVCaptureVideoPreviewLayer on screen. I want to change its position instantly after the user touches a button.
I am able to change its position using the following code, but it is not happening instantly but with some kind of smooth liner transition.
CGRect frame = self.preview.frame;
frame.origin.x = previewLeft;
frame.origin.y = previewTop;
self.preview.frame = frame;
How can I make it to change its position without any kind of intrinsic animation?
CALayer.frame is a computed property, but it cannot be explicitly animated. By setting it, you're also setting position, which is implicitly animated.
You can temporarily suppress all actions, the mechanism by which Core Animation creates its default layer animations:
Swift code:
CATransaction.begin()
CATransaction.setDisableActions(true)
self.preview.frame = frame;
CATransaction.commit()
Objective-C code:
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.preview.frame = frame;
[CATransaction commit];
LucasTizma's answer is perfect.
Besides, there is a trick to solve this issue.
Just slow down the window speed:
[(CALayer *)[[[[UIApplication sharedApplication] windows] objectAtIndex:0] layer] setSpeed:0.9];
Don't forget to correct it to 1.0f after your transition.

box flip 3D animation for side menu

I have scrollview and inside scrollview I have 2 views as MainView and another as SideMenuView.
What I want to make animation like below.
Any idea what needs to be done to get this working?
Psuedo Code below:
- (void)animateSideMenu{
homeView.frame = CGRectMake(sideMenuWidth, 0.0, (self.view.frame.size.width - sideMenuWidth), self.view.frame.size.height);
[UIView animateWithDuration:1.0 delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
sideMenu.frame = CGRectMake(0.0, 0.0, sideMenuWidth, sideMenuHeight);
[self flipAnimation];
} completion:^(BOOL finished) {
}];
}
- (void)flipAnimation{
CABasicAnimation *yRotate = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
yRotate.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 0, 1, 0)];
yRotate.toValue = #(M_PI * 1.5);
yRotate.duration = 0.5;
yRotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[sideMenu.layer addAnimation:yRotate forKey:#"yRotate"];
}
Below are the steps to develop this type of animation:
set the frame of homeView(Red colour view) of screen size & add pan gesture to this view.
set frame of sideMenu view in negative x-axis.
Create a responder function for pan gesture recogniser, in this function call the above mentioned animateSideMenu function.
Adjust the animation parameters accordingly.
Try with this & let me know if anything comes up.
I would not use a scrollview but rather a UIPanGestureRecognizer in which I would detect your current "pan offset" and calculate a current fraction of the animation and positions of those two views (let's simplify it so that the menu is view1 and the rest view2).
Then I would simply set an appropriate transform in the .Changed state.
In .Ended state I would see whether the menu is closer to being open or close and create an animation with .toValue set accordingly. You should use CAAnimations or better - Facebook pop animations because you can pause and remove them and the views stay put and not jump to their initial positions (as is often the case with UIViewAnimate).
If you need help with writing exact code you can write here and I'll edit my answer.
There's tutorial for exactly that menu, though it's in Swift:
https://www.raywenderlich.com/87268/3d-effect-taasky-swift

UIView not changing position in animateWithDuration

I have two UIViews (My bad it is a UIView and a UIButton) which I am animating at the same time. I originally had a view and a containerView which would animate just fine and worked like a charm.
Now only one of my UIViews will move/animate in animateWithDuration even though through debugging the frame of the other view says that it is in a position it is not.
CGRect rect = self.toggle.frame;
CGRect tabRect = self.tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:0.3 animations:^{ // animate the following:
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
NSLog(#"%f AfterAnimation", tabButton.frame.origin.x);
The toggle view moves fine, but the tabButton view does not animate or move. The strange thing is that both the "After" and "AfterAnimation" debugging code returns the same value, which suggests the frame has indeed moved. Is there a specific reason that this will not work when toggle is a UIView when it would work as a UIContainerView?
Note that if I remove the line
self.toggle.frame = rect;
tabButton will animate correctly, but if I move toggle, tabButton will not move regardless of whether it is first in the animation block or second.
Edit: I have tried moving them into separate blocks and to change the center point rather than the frame, to no avail. It seems that if the toggle view moves, the tabButton will not move.
Edit 2: The pictorial evidence.{
In the following screenshots tabButton bg is green and toggle bg is red.
Above: Initial position (toggle is off-screen) correct position
Above: The problem in question toggle is correct tabButton is not
Above: When self.toggle.frame = rect is commented out (tabButton correct, toggle not)
}
Edit 3: It's even worse than I feared.{
I have done a few more tests and even if I take the toggle change out of the animation block to make it an instant thing, the tabButton will still not animate. This makes me think the tabButton may just fundamentally dislike the toggle view and/or myself so will not move just to spite me.
}
Edit 4:{
If I change the tabButton animation to tabButton.frame = CGRectMake(10,10,100,100) the View snaps instantly to that location and animates back to its original position in the same time as the animation duration.
}
I better add more bookkeeping/TLDR information in case things aren't clear.
toggle is an instance of ToggleDraw which is a subview of UIView which I created.
tabButton is a UIButton which is part of my IB viewController and a property of the class
Both toggle and tabButton are subviews of self.view
The animations will work individually with no modifications to the logic of the rects but will not work if they are animated at the same time
toggle animation seems to take precedence over tabButton animation regardless of the order
I had a problem with the animation of an UIView created in IB (the animation didn't start from the current position of the view, and ended in the initial position).
All worked fine after sending layoutIfNeeded() to the underlaying view before the animation block:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5) { () -> Void in
...
I think it is not a problem about a UIView Animation. Maybe your toggle posiztion is related to your tabButton. For a try, your can set toggle frame to a rect lick (10, 10, 100,100), then check the result.
I've created an example of what you describe and everything seems to work fine. This is what I used:
UIView *toggle = [[UIView alloc] initWithFrame:CGRectMake(320, 64, 100, 100)];
[toggle setBackgroundColor:[UIColor redColor]];
[self.view addSubview:toggle];
UIButton *tabButton = [[UIButton alloc] initWithFrame:CGRectMake(220, 64, 100, 100)];
[tabButton setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:tabButton];
CGRect rect = toggle.frame;
CGRect tabRect = tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:1 animations:^{ // animate the following:
toggle.frame = rect; // move to new location
tabButton.frame = tabRect;
}];
What I can suggest is to make sure that the code is being ran on mainthread:
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
});
Also take into account that the log you have after the animation code is incorrect as it won't run after the animation, but rather right next to asking for the animation.
If you want code to run after the animation you should use:
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
} completion:^(BOOL finished){
NSLog(#"Finished animating!");
}];
I have found a solution to the problem. If I initialise the UIButton (tabButton) programmatically rather than through the interface builder, both views will animate simultaneously.
This however seems very hacky to me, kind of like putting a bandaid over a missing foot and hoping it will sort itself out.
I could not work out the root cause of the problem but at least I could avoid the symptoms.
If anyone knows why the views would not animate when the button was made in the interface builder post an answer here, I am interested in knowing the reason behind this.
Thanks for your help everyone.

Cancelling UIView Animation - self.view.layer removeAllAnimations not working

I've got a UIView Animation going on that I need to cancel in my iOS app. I've tried this:
[self.view.layer removeAllAnimations];
But it didn't work. The animation continued. Here is my animation code:
[UIView animateWithDuration:1.4 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
recognizer.view.transform = CGAffineTransformTranslate(recognizer.view.transform, translation.x, translation.y);
} completion:^(BOOL finished) {
NSLog(#"completed animation, now do whatever");
}];
Does anybody have any ideas as to why it's not working?
You are adding that animation to recognizer's view, hence you will have to remove it from that same view's layer.
So instead of
[self.view.layer removeAllAnimations];
you may want to
[recognizer.view.layer removeAllAnimations];
And to keep the current status of the transformation, fetch that one from the presentation layer. The presentation layer is the one that actually reflects the changes during the animation.
recognizer.view.layer.transform = recognizer.view.layer.presentationLayer.transform;
Ok - just figured it out. Changed the component beng animated from the gesture recogniser on top of the image view to the image view itself. Now, just before the code to stop the animation, I have:
truckView.frame = [[trackView.layer presentationLayer] frame];
[truckView.layer removeAllAnimations];
So this is the way to do it. Thanks for the help that led me to this answer,
Sam

Return view to original position when ending UIView animation

I have a very simple UIView animation, which causes my view to "throb":
[UIView animateWithDuration:0.2 delay:0.0f options:UIViewAnimationOptionAllowUserInteraction+UIViewAnimationOptionRepeat+UIViewAnimationOptionAutoreverse animations:^{
CGAffineTransform transf = CGAffineTransformScale(self.view.transform, 1.05f, 1.05f);
[self.view setTransform:transf];
} completion:nil];
At some point the user hits a button to cancel the animation, and apply a new transform.
[self.view.layer removeAllAnimations];
[self.view setTransform:aNewTransform];
I'd like it to reset to it's original transform, but instead it's getting increased in size by 5%.
Edit: I tried adding a completion block that resets the transform to it's original position. This works, but causes the transform I run immediately after to be trampled... the completion block gets run AFTER I apply aNewTransform.
Edit 2: I found a solution, using CABasicAnimation, instead of UIView animations. I would still be interested if anybody found a solution using UIView animations... I like the block-based interface better. This also only works, because I happen to be keeping track of my scale value separate from the one applied to the view. Everything that changes the scale uses a method that also changes self.scale
My replacement animation:
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
basicAnimation.toValue = [NSNumber numberWithFloat:self.scale*1.05f];
basicAnimation.fromValue = [NSNumber numberWithFloat:self.scale];
basicAnimation.autoreverses = YES;
basicAnimation.duration = 0.2;
basicAnimation.repeatCount = HUGE_VALF;
[self.view.layer addAnimation:basicAnimation forKey:#"Throb"];
Before starting a new animation on an existing view, you can reset the view if any of these attributes were previously altered:
// first, don't forget to stop ongoing animations for the view
[theView.layer removeAllAnimations];
// if the view was hidden
theView.hidden = NO;
// if you applied a transformation e.g. translate, scale, rotate..., reset to identity
theView.transform = CGAffineTransformIdentity;
// if you changed the anchor point, this will reset it to the center of the view
theView.layer.anchorPoint = CGPointMake(0.5, 0.5);
// if you changed the alpha, this will reset it to visible
theView.alpha = 1.;
Try putting the line
[self.view setTransform:aNewTransform];
in the completion block.

Resources