UIViewController half screen "drawer slide" animation - ios

I am trying to have a UIViewController that appears with a "slide" animation from the right. Not like a Push segue, not like the Facebook app. I want the new ViewController to slide ON TOP of the current one (not push it away), but only cover PART of the screen, leaving the other part showing the first ViewController.
What I have tried: The closest that I have gotten is by creating a custom segue with the following:
- (void)perform
{
__block UIViewController *src = (UIViewController *) self.sourceViewController;
__block UIViewController *dst = (UIViewController *) self.destinationViewController;
CATransition* transition = [CATransition animation];
transition.duration = .50;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromRight;
[src.navigationController.view.layer addAnimation:transition forKey:#"SwitchToView1"];
[src.navigationController pushViewController:dst animated:NO];
}
This achieves the animation that I am going for, but it covers the entire first ViewController. How would I make it stop at a certain point and not cover the entire thing?
I am using Storyboards, and I this is my first time trying any sort of new animation.

You can try doing it in your source view controller, and changing the frame for your destnation (the x axis) something like:
- (void) perform {
UIViewController *dst = (UIViewController *) self.destinationViewController;
[dst.view setFrame:CGRectMake(160, 0, YOUR_DST_WIDTH, YOUR_DST_HEIGHT)];
//your animation stuff...
[self addChildViewController:dst];
[self.view addSubview:dst.view];
[dst didMoveToParentViewController:self];
}
And that should do it!
Let me know if it didn't...
UPDATE!:
#CaptJak Hey, sorry that didn't work out for you.. I wrote the following code, and it works without any problems here.. I linked it to a button click.. try it and let me know! (PS: I added animations too!).
ViewController *tlc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainViewController"];
[tlc.view setFrame:CGRectMake(-320, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self addChildViewController:tlc];
[self.view addSubview:tlc.view];
[tlc didMoveToParentViewController:self];
[UIView animateWithDuration:0.3 animations:^{
[tlc.view setFrame:CGRectMake(-160, 0, self.view.frame.size.width, self.view.frame.size.height)];
}];

Using the answer given by Albara, I was able to also create a segue with the same animation. The custom segue is as follows:
- (void)perform
{
UIViewController *src = (UIViewController *)self.sourceViewController;
UIViewController *dst = (UIViewController *)self.destinationViewController;
[dst.view setFrame:CGRectMake(380, 0, dst.view.frame.size.width, dst.view.frame.size.height)];
[src addChildViewController:dst];
[src.view addSubview:dst.view];
[dst didMoveToParentViewController:src];
[UIView animateWithDuration:0.5 animations:^{
[dst.view setFrame:CGRectMake(300, 0, src.view.frame.size.width, src.view.frame.size.height)];
}];
}
Just a matter of renaming, really.

I just created NDOverlayViewController to do something like this. Actually it can overlay/slide one view controller over another from any edge with variable offset, extent and animation options. I created it as an experiment but maybe it will be helpful to somebody?

Related

Replace-root-view-controller custom segue with animation?

I've got a custom UIStoryboardSegue subclass which just replaces the root view controller with the destination VC. Works exactly as I want it to... however, I'd like to be able to add a transition animation, and I can't find any good examples of how to do that in the context of replacing the root VC.
The -perform selector of my class is this:
-(void)perform {
UIViewController *source = (UIViewController *)self.sourceViewController;
source.view.window.rootViewController = self.destinationViewController;
}
...How do I add a nice animated transition?
There are numerous ways you could add a "nice animation". Here is an example of a sort of card shuffle animation where one view moves up and left, while the other moves down and right, then reverses after changing the z-order of the two views. This implementation inserts the destination controller's view into the window's view hierarchy under the source controller's view.
-(void)perform {
CGFloat dur = 1.0;
UIView *destView = [self.destinationViewController view];
UIView *srcView = [self.sourceViewController view];
CGFloat viewWidth = srcView.frame.size.width;
CGPoint center = srcView.center;
AppDelegate *appDel = [[UIApplication sharedApplication] delegate];
destView.frame = srcView.bounds;
[appDel.window insertSubview:destView belowSubview:srcView];
[UIView animateWithDuration:dur animations:^{
srcView.frame = CGRectOffset(srcView.frame, -viewWidth/1.9, -20);
destView.frame = CGRectOffset(destView.frame, viewWidth/1.9, 20);
} completion:^(BOOL finished) {
[appDel.window bringSubviewToFront:destView];
[UIView animateWithDuration:dur animations:^{
destView.center = center;
srcView.center = center;
} completion:^(BOOL finished) {
[srcView removeFromSuperview];
appDel.window.rootViewController = self.destinationViewController;
}];
}];
}

How to change the transition animation from bottom to top in navigation controller?

I am new to iOS. I am trying to change the transition animation in navigation controller to load a new UIView controller from bottom to top, using Segue. I believe it will not be too hard to implement but may be I am not able to understand it.
I could not find any solution in other posts.
Here's the code I've been trying:-
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"alarmSegue"]) {
tab2ViewController *destViewController = segue.destinationViewController;
UIView *destView = destViewController.view;
destViewController.selectionName = #"alarms";
[sender setEnabled:NO];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView animateWithDuration:0.0
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
[destView setTransform:CGAffineTransformMakeTranslation(0, -1000)];
//[destView setFrame:CGRectMake(0, 440, 400, 45)];
//destView.frame = CGRectMake(0, 0, 320, 460);
}
completion:^(BOOL finished){
[sender setEnabled:YES];
}];
[UIView commitAnimations];
}
}
I just want to implement a SIMPLE transition from bottom to top, using Segue. I want to set some properties of destination controller as well.
Replace the code between [UIView beginAnimations:nil context:nil]; and [UIView commitAnimations]; (including those two lines as well) with the following:
CGAffineTransform baseTransform = destView.transform; //1
destView.transform = CGAffineTransformTranslate(baseT,0,destView.bounds.size.height); //2
[UIView animateWithDuration: 0.5 animations:^{
destView.transform = baseTransform; //3
}];
Make a reference of the original transform.
Push our view down without an animation so we can animate it back up
Set the original transform back to the view. This time, it'll animate
You can further add the delay, options and completion parameters to that UIView Animation method. Xcode will probably help you with the autocompletes on those.
Well you are nearly there.
You need to change two things mainly:
1 Change the initial position of the view. You want the view to appear from bottom to top, so initially your view should be at the bottom. This piece of code should be written before the animation.
[destView setTransform:CGAffineTransformMakeTranslation(0, -1000)];
2 Then you need to slide from bottom to the original position , hence inside the animation block:
[destView setTransform:CGAffineTransformMakeTranslation(0, 0)];
One more suggestion, I guess you are using a newer version of iOS(above iOS4) instead of using animation blocks use UIView's animation method
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
For further info, refer description.
EDIT :
According to your edit on question, you are looking to change the transition animation for NavigationController in the app using storyboard. For that you could use Custom Segue. You will have to create a custom segue, a class that subclasses UIStorySegue and override its perform method like this:
-(void)perform {
//UIViewAnimationOptions options;
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = 0.9f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
transition.subtype = kCATransitionFromTop; //kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom
[destinationController.view.layer addAnimation:transition forKey:kCATransition];
[sourceViewController presentViewController:destinationController animated:NO completion:nil];
}
You have to change the segue class to this custom segue in storyboard as well, to make it function.
Try using transition effects
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"alarmSegue"]) {
CATransition transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = direction;
[self.view.layer addAnimation:transition forKey:kCATransition];
tab2ViewController *destViewController = segue.destinationViewController;
UIView *destView = destViewController.view;
destViewController.selectionName = #"alarms";
[sender setEnabled:NO];
}
}

Custom ViewController Transition Remove Gap Between Views

Hi I have a custom transition between two view controllers and I want to remove the black gap between them when it makes the transition. What can I do to do this? Thank you! Here is how the transition is performed currently with a CATransition and a picture of the gap.
- (void)bottomButtonScreen4:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"Swipe Up Worked");
settingsViewController = [[SettingsViewController alloc] init];
CATransition* transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromTop;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
settingsViewController = [self.storyboard instantiateViewControllerWithIdentifier: #"settingView"];
[self.navigationController pushViewController:settingsViewController animated:NO];
}
Using this transition, there's only so much you can do. You'll notice (if you make it slow enough) that as the new view moves in, it's also fading in and the outgoing view is fading out. You can get rid of the black bar by setting the window's background color to the same color as your incoming view's background color, but you'll also see the outgoing controller's view fade to that color as it goes out. Check that out, and see if that's a look you can live with.
self.view.window.backgroundColor = settingsViewController.view.backgroundColor;
P.S. You should get rid of the line where you alloc init settingsViewController. It isn't doing anything now, and it was the wrong way to get that instance anyway.
After Edit:
Another way to do this that gets rid of the black bar problem, is to use animateWithDuration like this:
-(IBAction) pushViewControllerFromTop {
CGFloat height = self.view.bounds.size.height;
UIViewController *settingsViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Blue"];
settingsViewController.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y - height, self.view.frame.size.width, self.view.frame.size.height);
[self.view.superview addSubview:settingsViewController.view];
[UIView animateWithDuration:.5 animations:^{
self.view.center = CGPointMake(self.view.center.x, self.view.center.y + height);
settingsViewController.view.center = CGPointMake(settingsViewController.view.center.x, settingsViewController.view.center.y + height);
} completion:^(BOOL finished) {
[self.navigationController pushViewController:settingsViewController animated:NO];
}];
}

IOS Animations without navigation controller

In my app im showing different screen this way:
info = [[Info alloc]initWithNibName:#"Info" bundle:nil];
[self.view addSubview:info.view];
Info is a subclass of UIViewController, so i just create it and add it as a subview of the current view.
When i want to dismiss it i just do:
[self.view removeFromSuperview];
Im not sure if this is the right way of doing it, but yesterday apple has published on app store another app that do the same to display views.
My question is, how can i animate the transition between views, so when i call addsubview it do the animation?
I tried this:
info = [[Info alloc]initWithNibName:#"Info" bundle:nil];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight
forView:info.view
cache:YES];
[self.view addSubview:info.view];
[UIView commitAnimations];
But its not working, this dont do anything.
It's not the right way to manage view hierarchy. You should use view controller methods for this (addChildViewController: & removeFromParentViewController). Then you can use method transitionFromViewController:toViewController:options:animations:completion: (or something like that) to do various animations.
to add a view controller's view use this
ChildViewController * child = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight
forView:info.view
cache:YES];
[self.view addSubview:child.view];
[UIView commitAnimations];
[self addChildViewController:child];
[child release];//for NON ARC
to remove view use this...
for (UIViewController *childVC in [self childViewControllers])
{
[childVC removeFromParentViewController];
}
[self.view removeFromSuperview];// YOU CAN ADD ANIMATIONS HERE
[self removeFromParentViewController];
HOPE this will help you, It is working fine for me....
You can use CATransition for that kind of animation without using navigationcontroller below is the code
Info *infoViewController = [[Info alloc]init];
CATransition *transition = [CATransition animation];
transition.duration = 0.50;
transition.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromRight;
UIView *containerView = self.view.window;
[containerView.layer addAnimation:transition forKey:nil];
[self presentModalViewController:infoViewController animated:NO];
Like this you can animate the view from left to right without using navigationcontroller.
You can give UIView Block Animation a shot. Something like this:
[UIView animateWithDuration:.25 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^
{
requiredView.frame = CGRectMake(0, 300.0, 400.0, 180.0);
} completion:^(BOOL finished)
{
}];
You can set the view's frame and position it outside the viewing co-ordinates and then change the frame accordingly to animate the UIView.

ECSlidingViewController problems with topview animation

I'm using ECSliding and I have this problem!
In my project there are this files:
InitViewController (ECSlidingController)
FirstViewController (UIViewController)
SecondViewController (UIViewController)
LeftMenuViewController (UIViewController)
I use InitView to instatiate my FirstView as the topview.
In my FirstView there is a button, when pressed sets SecondView as topview.
Is it possible to animate the changing topview like I'm opening a new view?
or how can I open a new view that uses ECSliding like the first one?
I'm using this code to change topview:
self.slidingViewController.topViewController = second;
[self.slidingViewController resetTopView];
The animation that I want could just be the default one, like:
[self presentViewController:(*UIViewController) animated:YES completion:nil];
Subclass ECSlidingViewController. I call my subclass DeckController:
In DeckController.h:
- (void)setTopViewController:(UIViewController *)topViewController withTransition:(CATransition*)transition;
In DeckController.m:
- (void)setTopViewController:(UIViewController *)topViewController withTransition:(CATransition*)transition {
self.topViewController = topViewController;
[UIView animateWithDuration:0.25f animations:^() {
[[self.view layer] addAnimation:transition forKey:#"topViewTransition"];
}];
}
Then in the view controller that wants to switch to the new top view, do something like this:
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromRight];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setFillMode:kCAFillModeBoth];
[animation setDuration:.25];
DeckController *deck = (DeckController*)self.slidingViewController;
[deck setTopViewController:newTopViewController withTransition:animation];
Probably your best option is to use the animation methods of the sliding view controller. Animate the top view off screen, in the completion block change the top view controller (to second) then start a new animation to animate the top view back onto screen.
[self.slidingViewController anchorTopViewOffScreenTo:...
animations:...
onComplete:^{
self.slidingViewController.topViewController = second;
[self.slidingViewController resetTopView];
}];
I just use same operation as when Im clicking on a menuitem in my left menu.
[self.anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.topViewController.view.frame;
self.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
self.topViewController.view.frame = frame;
[self resetTopView];
}];
Just generalize this, and you can call it everywhere. It does the animation by itself.
You can do as #Alex, but if you don't have to do some custom animation, this is all you need.
NB. in my example Im using this in the initViewController (for which is the ECSlidingViewController, therefore i'm sending to self), for forcing user to a specific viewController. If you need to change view in any subViewControllers you just sending to self.slidingViewController like the example below:
[self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
self.slidingViewController.topViewController.view.frame = frame;
[self.slidingViewController resetTopView];
}];
You can manually create the animation then set the topViewController at the end so that the sliding view controller is in the state that you expect.
SecondTopViewController *secondTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondTop"];
secondTopViewController.view.frame = self.slidingViewController.view.bounds;
secondTopViewController.view.alpha = 0;
// temporarily add the second top view so that we can animate it in.
[self.slidingViewController.view addSubview:secondTopViewController.view];
[UIView animateWithDuration:0.5 animations:^{
secondTopViewController.view.alpha = 1;
} completion:^(BOOL finished) {
// remove the second top view.
[secondTopViewController.view removeFromSuperview];
// let sliding view controlller take over from here
self.slidingViewController.topViewController = secondTopViewController;
}];
See my working example here: https://github.com/enriquez/ECSlidingViewController/tree/so-16658539
#Michael-Enriquez's answer converted to swift.
let viewController = (self.storyboard?.instantiateViewControllerWithIdentifier("ViewController"))! as? ViewController
let navController = UINavigationController(rootViewController: viewController!)
navController.view.frame = self.slidingViewController().view.bounds
navController.view.alpha = 0
self.slidingViewController().view.addSubview(navController.view)
UIView.animateWithDuration(0.25, animations: {
navController.view.alpha = 1
}, completion: {(finished:Bool) -> Void in
navController.view.removeFromSuperview()
self.slidingViewController().topViewController = navController
})

Resources