I am brand new to Core Animation, and I need to know how to do 2 animations:
I need to switch XIBs by fading through black (fully releasing the the first view controller)
I need to mimic the UINavigationController's pushViewController animation (switching XIBs and releasing the first view controller)
How can you achieve these animated view transitions?
I've done both of these animations, but maybe not in the exact way you are looking for.
Fade View to black, I took this the other way an instead added a new
subview that covered the entire window that was Black and animated
the Alpha from 0.0 to 1.0. Made for a nice effect.
[UIView animateWithDuration:0.5
animations:^{ _easterEgg.alpha = 1.0; }
completion:^(BOOL finished) { [self animateIndex:0]; }];
Slide in a view like UINavigationController. I didn't do this exactly like UINavigationController since it does multiple animations, but I did have a new view slide the previous view off screen. This code sets the frame of the new view off screen to the right of the current view, builds a frame location that is off the screen to the left, and grabs the current visible frame. Finally it just animates the new view from off screen right into the visible frame, and the old view from the visible frame to off left. Then removes the old view.
CGRect offRight = CGRectMake(_contentView.frame.size.width,
0,
_contentView.frame.size.width,
_contentView.frame.size.height);
CGRect offLeft = CGRectMake(-_contentView.frame.size.width,
0,
_contentView.frame.size.width,
_contentView.frame.size.height);
CGRect visibleFrame = CGRectMake(0, 0, _contentView.frame.size.width, _contentView.frame.size.height);
[view setFrame:offRight];
UIView *currentView = [[_contentView subviews] lastObject];
[_contentView addSubview:view];
[UIView animateWithDuration:0.5
animations:^{
[currentView setFrame:offLeft];
[view setFrame:visibleFrame];
}
completion:^(BOOL finished) {
[currentView removeFromSuperview];
}];
Related
I'm building an iOS 8 app and using UIPresentationController to present a view controller in a custom way. (see my previous question about this here: Replicating the style of the iOS Mail App's Compose Function).
The issue I'm having is that when I present the controller, the navigation bar starts off as 64 points tall and then jumps/shrinks back to 44 once its presentation is finished. My guess is that the view controller realizes it is not covering the status bar and so it shrinks itself down once it comes to its final resting position. I'd like for the navigation bar to be 44 points tall the entire time and not jump/shrink.
The image below is what the view controller looks like at the end of the presentation. It is also what I want it to look like the entire time. Any thoughts on how to keep the navigation bar at 44 points the entire time?
UPDATE (3/24/2015):
I referenced a blog post from a while back to find some more information on this issue. Basically, UINavigationController draws its navigation bar either 64 or 44 points tall depending on if its view's frame is matched up with the app's window or not. So I need some way of telling the navigation controller that its final resting position will not be lined up with the window, and that the nav bar should be drawn 44 points tall.
http://blog.jaredsinclair.com/post/61507315630/wrestling-with-status-bars-and-navigation-bars-on
Finally found an answer to this question. It's explained in this previous stack overflow post:
Navigation bar gets adjusted after calling completeTransition: in custom transition
Thank you for not making me use my hard earned rep to start a bounty!
I had an issue a bit like yours, where the navigation bar would resize after [transitionContext completeTransition:YES] was called, based on visual contiguity of the navigationBar's frame sharing a border with the UIWindow's top. My navigation bar was nowhere near the top, so it resized itself to 44px instead of the normal "extend-under-the-status-bar" 64px. To get around this, I simply completed the transition before I animated my toViewController's alpha and position. That is, once everything was positioned properly to be animated in, I called completeTransition: to let the navigationController adjust itself while invisible. So far, this hasn't had any unintended side-effects, and the additional alpha in, move frame animations still continue after you completeTransition.
Here is my animateTransition: method in my presentation animator class that conforms to <UIViewControllerAnimatedTransitioning>
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *presentedViewController = self.presenting ? toViewController : fromViewController;
UIView *containerView = [transitionContext containerView];
NSTimeInterval animationDuration = [self transitionDuration:transitionContext];
if (self.presenting) {
containerView.alpha = 0.0;
presentedViewController.view.alpha = 0.0;
[containerView addSubview:presentedViewController.view];
[UIView animateWithDuration:animationDuration delay:0 options:kNilOptions animations:^{
containerView.alpha = 1.0;
} completion:^(BOOL finished) {
presentedViewController.view.frameTop += 20;
//I complete the transition here, while my controller's view is still invisible,
// but everything is in its proper place. This effectively positions everything
// for animation, while also letting the navigation bar resize itself without jarring visuals.
[transitionContext completeTransition:YES];
//But we're not done quite yet...
[UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
presentedViewController.view.frameTop -= 20;
presentedViewController.view.alpha = 1.0;
} completion:nil];
}];
}
if (!self.presenting) {
[UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
presentedViewController.view.alpha = 0.0;
presentedViewController.view.frameTop += 20;
} completion:^(BOOL finished) {
[UIView animateWithDuration:animationDuration delay:0 options:kNilOptions animations:^{
containerView.alpha = 0.0;
} completion:^(BOOL done) {
[transitionContext completeTransition:YES];
}];
}];
}
Hope this helps anyone that finds themselves in my position!
I am trying to animate uiview. That is, on button click view will appear through animation. This works properly. But when I try to remove that view through animation, animation is applied on the view and not on its sub controls. That is, view gets disappeared but all its sub controls are viewable.
So my question is how can I remove uiview along with its subcontrol using animation. My current code is:
To animate uiview code:
[UIView animateWithDuration:.5f animations:^{
CGRect theFrame = self.viewFilter.frame;
theFrame.size.height += 140.f;
self.viewFilter.frame = theFrame;
}];
To hide uiview:
[UIView animateWithDuration:.5f animations:^{
CGRect theFrame = self.viewFilter.frame;
theFrame.size.height -= 140.0f;
self.viewFilter.frame = theFrame;
}];
Thanks in advance
make 'viewFilter' super view for that all sub controllers , means add that all sub controllers to viewFilter
I'm trying to do a very simple transition: one view moves half the screen to the left while the second ("to") view moves in half a screen.
I have the animation working, but when I reverse the animation, I see a flickering. The "to" view (i.e. the original view) is visible at the origin of 0,0 although I set a different frame.
I dumped the view hierarchy. The frames are set correctly (-100 0; 320 480 for the to view), but nonetheless it shows up at 0,0. Is a screenshot of the view cached somewhere for the animation?
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
CGRect offsetCoverRect = CGRectMake(-100.0, 0.0, 320, 480);
CGRect detailsRect = CGRectMake(-100.0 + 320.0, 0.0, 320, 480);
CGRect detailsOutsideRect = CGRectMake(320.0, 0.0, 320, 480);
CGRect normalRect = CGRectMake(0.0, 0.0, 320, 480);
if (self.revealDetails)
{
toViewController.view.frame = detailsOutsideRect;
[container addSubview:toViewController.view];
}
else
{
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
// reversing… set the frame to the original offset (shows at 0,0 for a moment)
toViewController.view.frame = offsetCoverRect;
}
[UIView animateKeyframesWithDuration:2 delay:0 options:0 animations:^{
if (self.revealDetails)
{
fromViewController.view.frame = offsetCoverRect;
toViewController.view.frame = detailsRect;
}
else
{
fromViewController.view.frame = detailsOutsideRect;
toViewController.view.frame = normalRect;
}
} completion:^(BOOL finished) { [transitionContext completeTransition:finished]; }];
}
Update:
It seems to be related to UIModalPresentationCustom. I need to use this so that the from view is not removed when the transition completes. However, it seems to assume that the from view controller for the reverse transition starts at 0,0.
Update 2:
Very easy to reproduce with the following code:
UIView *snapshot = [containerView snapshotViewAfterScreenUpdates:NO];
[containerView addSubview:snapshot];
The above will show the to view centered on screen, no matter what actual frame or center I set before the animation.
I know this is an old question, but I ran into the same problem and after hours of struggling, finally came up with a solution:
In the completion block of the present animation,
Create a snapshot of the fromViewController view
Perform frame changes/transforms on the snapshot
Add snapshot to the containerView
Remove the fromViewController view from the container view
In the completion block of the dismiss animation (or anywhere if its a dismiss?), remove the snapshot from the container
By removing the fromViewController's view from the container view, it does not get reset to it's initial position because it is no longer inside of the container view.
This is because your animateTransition method is being called from background.
Call your method in this way
dispatch_async(dispatch_get_main_queue(), ^
{
[self animateTransition]; // your method goes here
});
You should never call any animation or any UI related methods on background thread
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.
During a sliding animation(down, pause, then up back to the original position) of a subview, the device is rotated, so the superview is rotated. I want to keep the subview's width the same as the superview, so I need to resize it during its sliding animation.
Here is the sliding animation code:
[UIView animateWithDuration:0.3 animations:^{
self.frame = finalFrame;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:3 options:0 animations:^{
self.frame = initFrame;
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}];
This is the method that is called when I detect rotation:
- (void)rotate:(NSNotification *)notif {
// What to do here to adjust the width but also keep the sliding animation going.
}
It seems there is no auto-resizing magic that can be used here. One must:
Record the progress of the animations.
On detection of rotations, cancel the old animations, adjust the view size, and add new animations starting from the current progress.
Here is a sample project for reference: http://d.pr/f/M4UW.
You can animate the bounds of the layer to change the width. Just make the height the same and apply an animation for the bounds.
If you want both animations to have the same duration, timing function etc. then you could add them both to an animation group and add that group to the layer you are animating.