How to use animateWithDuration: when presenting view controller on iOS 7? - ios

I'm currently using the following code in order to present my view controller.
CATransition* transition = [CATransition animation];
transition.duration = 1;
transition.type = kCATransitionFade;
transition.subtype = kCATransitionFromBottom;
[self.view.window.layer addAnimation:transition forKey:kCATransition];
[self presentViewController:viewController animated:NO completion:nil];
I need to use more complex animation utilizing UIView animateWithDuration method. It is just modal presentation and background view controller should stay there. How can I animate presenting view controller's view?
Update: next version of code :)
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:NULL];
RootViewControllerEx *viewController = (RootViewControllerEx *)[sb instantiateViewControllerWithIdentifier:#"SB_RDVC"];
viewController.transitioningDelegate = self;
viewController.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController:viewController animated:YES completion:nil];
...
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
...
}
VC in storyboard doesn't have any segue specified, it stays lonely in storyboard :)

You need to use UIModalPresentationCustom as your UIViewController's modalPresentationStyle.
You then need to create a class that conforms to the UIViewControllerTransitioningDelegate and set that your on presentedViewController.
Example
YourViewController *viewController = [[YourViewController alloc] init];
viewController.delegate = self;
viewController.transitioningDelegate = self;
viewController.modalPresentationStyle = UIModalPresentationCustom;
[self.navigationController presentViewController:viewController animated:YES completion:nil];
You must then implement the following method:
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
Which returns an object that conforms to a UIViewControllerAnimatedTransitioning protocol, that implements the following method:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIView *inView = [transitionContext containerView];
UIView *toView = [[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey] view];
toView.frame = //CGRectMake(0,0,0,0);
[inView addSubview:toView];
// Use whatever animateWithDuration you need
[UIView animateWithDuration:0.6f delay:0.0f usingSpringWithDamping:0.7f initialSpringVelocity:0.5f options:UIViewAnimationOptionCurveEaseIn animations:^{
// Custom animation here
} completion:^(BOOL finished) {
// IMPORTANT
[transitionContext completeTransition:YES];
}];
}
Always remember to tell the context when a transition is complete, otherwise you'll enter an unstable state.

Related

How to POP a viewcontroller with custom back UIbutton?

I am using side drawer menu https://github.com/mutualmobile/MMDrawerController
I cannot come to back with custom UIButton.
- (IBAction)doBackAction:(id)sender {
NSArray *viewControllers=self.navigationController.viewControllers;
for (UIViewController *viewController in viewControllers) {
NSLog(#"%#",NSStringFromClass([viewController class]));
UINavigationController *obj=(UINavigationController *)[self.mm_drawerController centerViewController];
}
UINavigationController *nav =
(UINavigationController *)self.mm_drawerController.centerViewController;
[nav popToRootViewControllerAnimated:NO];
[self.navigationController popViewControllerAnimated:YES];
[self.mm_drawerController closeDrawerAnimated:YES completion:nil];
}
i have tried this also https://github.com/mutualmobile/MMDrawerController/issues/195
I cannot make it work.
You might consider setting a global variable (e.a. on a singleton or in a database) which ViewController you came from with viewDidDissapear:
- (void)viewDidDissapear {
Singleton.lastViewController = [self class];
}
From there you can do this to know where you came from (e.a. an IBAction):
// Set your global variable: `NSString` Singleton.lastViewController
[self presentViewController:[[UIStoryboard storyboardWithName:#"Main_iPad" bundle:nil] instantiateViewControllerWithIdentifier:Singleton.lastViewController] animated:YES completion:nil];
I use this for apps that have custom segues and programmed navigation.
Please try this one. May be its help to you
for (UIViewController *controller in self.navigationController.viewControllers)
{
if ([controller isKindOfClass:[CartVC class]])
{
//Do not forget to import CompareCarsVC.h
// CATransition *transition = [CATransition animation];
// transition.duration = 0.5;
// transition.type = kCATransitionFade;
//
// [self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popToViewController:controller
animated:NO];
break;
}
}

PresentViewController with custom animation

I need to present view controller with UIViewAnimationOptionCurveEaseIn animation where source controller disappear from 1 alpha to 0 and destination controller appear from 0 alpha to 1
I'd like to use
presentViewController: animated: completion:
I've tried:
In source controller
UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
PopViewController *pop = [st instantiateViewControllerWithIdentifier:name];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:pop];
nav.navigationBarHidden = YES;
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.view.alpha = 0.f;
} completion:^(BOOL finished) {
[self presentViewController:nav animated:NO completion:nil];
}];
In destination controller
- (void)viewDidLoad{
[super viewDidLoad];
self.view.alpha = 0.f;
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.view.alpha = 1.f;
} completion:^(BOOL finished) {
}];
but controllers disappear and appear not at the same time
Pavel, for example
-(void)goToViewControllerAction
{
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
CATransition *transition = [CATransition animation];
[transition setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
transition.duration = 1.5;
[transition setType:kCATransitionFade];
[self.navigationController.view.layer addAnimation:transition forKey:#"someAnimation"];
[self performSegueWithIdentifier:#"backPopsegue" sender:nil];// Here your push or performSegue transition
[CATransaction commit];
}
Don't remember to use QuartzCore)
You can customize your transition like you want, there are a lot of different settings in it....

Sliding a view controller from bottom with a Gesture

I have a question (Objective-C related).
I want to create a sliding from bottom ViewController on top of the Root Map View Controller by a click on the annotation.
1) After a click on the annotation it should slide from bottom and show 10% of the height;
2) after an upward swipe gesture - it should show up to 100% if the user drags it upwards fully;
3) on the downward gesture user should be able to decrease its visible height to 10% again;
4) on the MapView click the bottom ViewController should hide.
I'm including the Visual scheme of the process implementation.
Any ideas are very very appreciated!
check this demo
NHSlidingController
PanningViewController
You can always try to write your own segue animation to transition between ViewControllers. Just make a custom UIStoryboardSegue, and override its perform method. Here is a snippet to start with:
#interface HorizontalSegue : UIStoryboardSegue
#property CGPoint originatingPoint;
#end
#implementation VerticalSegue
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
NSArray* windowArray = [UIApplication sharedApplication].windows;
if (windowArray.count > 0) {
UIWindow* appWindow = [windowArray lastObject];
[appWindow insertSubview:destinationViewController.view aboveSubview:sourceViewController.view];
}
else
[sourceViewController.view addSubview:destinationViewController.view];
// Store original centre point of the destination view
CGPoint originalCenter = destinationViewController.view.center;
// Set center to start point of the button
destinationViewController.view.center = CGPointMake(self.originatingPoint.x, self.originatingPoint.y*3);
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
destinationViewController.view.center = originalCenter;
}
completion:^(BOOL finished){
[sourceViewController presentViewController:destinationViewController animated:NO completion:NULL]; // present VC
}];
}
#end
Using this you can easily make custom animations. I am not totally sure if the part with adding the ViewController to the window is correct, but this is something that you could try and check/correct yourself.
Another way of animating segues is using CATransition:
-(void)perform
{
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = 0.25;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
transition.subtype = kCATransitionFromLeft; //kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom
[destinationController.view.layer addAnimation:transition forKey:kCATransition];
[sourceViewController presentViewController:destinationController animated:NO completion:nil];
}

Push segue custom animation (Navigation controller)

I have this storyboard :
TabBarController : Tab1 > NavigationController > VC1 > VC2 > VC3
// VC1.m
[self performSegueWithIdentifier:#"VC2" sender:self];
// VC2.m
[self performSegueWithIdentifier:#"VC3" sender:self];
I would like the transition between VC1 and VC2 to be a fade animation, but the transition between VC2 and VC3 the default one.
But I have to do a push segue and not modal to keep the benefits of UINavigationController (unless I'm missing something). All the solution I find for this are using presentViewController.
Try using this CATransition
CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
[navigationController.view.layer addAnimation:transition forKey:kCATransition];
isPush ? [navigationController pushViewController:viewController animated:NO] : [navigationController popViewControllerAnimated:NO];
If you insist on a segue than I see 2 ways:
1) Use UIViewControllerAnimatedTransitioning protocol and customize the transitions the way U like.
2) Write a custom UIStoryboardSegue
Or perform you own transitions and animations by means of manipulation of controllers and their views and frames smth like:
- (void)transitionToChildViewController:(UIViewController *)toViewController {
UIViewController *fromViewController = ([self.childViewControllers count] > 0 ? self.childViewControllers[0] : nil);
if (toViewController == fromViewController || ![self isViewLoaded]) {
return;
}
UIView *toView = toViewController.view;
[toView setTranslatesAutoresizingMaskIntoConstraints:YES];
toView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
toView.frame = self.privateContainerView.bounds;
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
[self.privateContainerView addSubview:toView];
[fromViewController.view removeFromSuperview];
[fromViewController removeFromParentViewController];
[toViewController didMoveToParentViewController:self];
}
the last method perhaps is the most tedious.

Swap views in a container view

I'd like to swap two custom UIViewControllers inside a ContainerView. To do this I created a ViewController that handles the logic of the swapping. This swap should occur when pressing a button.
What I want to do is similar to this. I tried the current accepted answer but the problem is that the layout doesn't show up. I don't get any compile errors or warnings.
I have this code in the ContainerViewController, executed when the button is pressed.
if([view isEqualToString:#"view1"]){
NSString *userId = [self getUserId];
ViewController1 *vc = [[ViewController1 alloc]init];
[vc someFunction:userId];
UIViewController *fromViewController = [self.childViewControllers firstObject];
[self swapViewController: fromViewController toViewController:vc];
}else{
NSString *userId = [self getUserId];
ViewController2 *vc = [[ViewController2 alloc]init];
[vc otherFunction:userId];
UIViewController *fromViewController = [self.childViewControllers firstObject];
[self swapViewController: fromViewController toViewController:vc];
}
The swapViewController function looks like this:
- (void)swapViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
toViewController.view.frame = fromViewController.view.frame;
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
}
completion:^(BOOL finished) {
[fromViewController removeFromParentViewController];
[toViewController didMoveToParentViewController:self];
}];
}
This is what I have in the storyboard:
If I just simply load the segue it works fine, but I need to show some data received over the internet, so I think it's not an option.
Could anyone please, give me directions on how to achieve that?

Resources