I'm checking out the new Xcode 6 Storyboards for os x application
I want to do the same like when doing push segue in iOS side.
But the new Xcode storyboards for os x application doesn't have the push segue.
So you can see there is no push. How to achive the same functionality?
I haven't tested it, but I've used a similar approach on my iPad app. It needs some tailoring for Cocoa:
-(void) perform {
UIView *source = ((UIViewController *)self.sourceViewController).view;
UIView *destination = ((UIViewController *)self.destinationViewController).view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
destination.center = CGPointMake(source.center.x + source.frame.size.width, destination.center.y);
[window insertSubview:destination aboveSubview:source];
[UIView animateWithDuration:0.4
animations:^{
destination.center = CGPointMake(source.center.x, destination.center.y);
source.center = CGPointMake(0- source.center.x, destination.center.y);}
completion:^(BOOL finished){
[source removeFromSuperview];
window.rootViewController = self.destinationViewController;
}];
}
Let me know if it works.
Depending on what you are trying to do, a NSTabViewController might be appropriate. It lazy-loads view controllers and can transition using a slide animation.
I have a UITextfield dedicated to dates. When clicked I have a method that shows a UIView with a UIDatePicker as a subview and a method to hide it again. By default this UIView is hidden.
I've noticed that the textfields of my form are no longer clickable. However my UIButtons are still working.
The UIView is a subview of the superview that the UITextFields and UIButtons are also part of. The UIView also takes up the whole area of the window.
The UIView is an outlet also.
Does anyone have any idea what is happening here?
Kind regard
Code:
- (void) hidePickerView {
[UIView animateWithDuration:0.5
animations:^{
[[self datePickerView] setFrame:CGRectMake(0, -250, 320, 50)];
} completion:^(BOOL finished) {
[[self datePickerView] removeFromSuperview];
}];
}
I've also tried putting the following in my viewDidLoad method:
[[self datePickerView] removeFromSuperview];
I was returning NO in my - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField method. Setting this to yes solved the problem.
So. Just started transitioning my IOS code to IOS7, and ran into a bit of problem.
I've got a UINavigationController, which has child ViewControllers and I'm using pushViewController to display the next views. To create a parallax animation with a set of images, if customized the UINavigationController to animate a set of UIImageViews and my child ViewControllers all have a self.backgroundColor = [UIColor clearColor], transparency.
Since iOS7, the way the UINavController animates it child vc's, is updated, by partially moving the current view controller and on top pushing the new viewcontroller, my parallax animation looks crap. I see the previous VC move a bit and then disappear. Is there any way I can restore the previous UINavigationController pushViewController animation? I can't seem to find this in the code.
WelcomeLoginViewController* welcomeLoginViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"WelcomeLogin"];
[self.navigationController pushViewController:welcomeLoginViewController animated:YES];
Even tried using:
[UIView animateWithDuration:0.75
animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[self.navigationController pushViewController:welcomeLoginViewController animated:NO];
[UIView setAnimationTransition:<specific_animation_form> forView:self.navigationController.view cache:NO];
}];
Does anyone have any clue?
I managed to workaround the new transition type by creating a category for UINavigationController. In my case I needed to revert it to the old transition style because I have transparent viewControllers that slide over a static background.
UINavigationController+Retro.h
#interface UINavigationController (Retro)
- (void)pushViewControllerRetro:(UIViewController *)viewController;
- (void)popViewControllerRetro;
#end
UINavigationController+Retro.m
#import "UINavigationController+Retro.h"
#implementation UINavigationController (Retro)
- (void)pushViewControllerRetro:(UIViewController *)viewController {
CATransition *transition = [CATransition animation];
transition.duration = 0.25;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[self.view.layer addAnimation:transition forKey:nil];
[self pushViewController:viewController animated:NO];
}
- (void)popViewControllerRetro {
CATransition *transition = [CATransition animation];
transition.duration = 0.25;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[self.view.layer addAnimation:transition forKey:nil];
[self popViewControllerAnimated:NO];
}
#end
I have the same problem with clear background colors and crappy animations, so I create custom transitioning for ViewController with new iOS7 API. All you need is simply set a delegate for your navigation controller:
// NavigationController does not retain delegate, so you should hold it.
self.navigationController.delegate = self.navigationTransitioningDelegate;
Just add this files into your project: MGNavigationTransitioningDelegate.
I had a problem where when UIViewController A did a pushViewController to push UIViewController B, the push animation would stop at about 25%, halt, and then slide B in the rest of the way.
This DID NOT happen on iOS 6, but as soon as I started using iOS 7 as the base SDK in XCode 5, this started happening.
The fix is that view controller B did not have a backgroundColor set on its root view (the root view is the one that is the value of viewController.view, that you typically set in loadView). Setting a backgroundColor in that root view's initializer fixed the problem.
I managed to fix this as follows:
// CASE 1: The root view for a UIViewController subclass that had a halting animation
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
// Do some initialization ...
// self.backgroundColor was NOT being set
// and animation in pushViewController was slow and stopped at 25% and paused
}
return self;
}
// CASE 2: HERE IS THE FIX
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
// Do some initialization ...
// Set self.backgroundColor for the fix!
// and animation in pushViewController is no longer slow and and no longer stopped at 25% and paused
self.backgroundColor = [UIColor whiteColor]; // or some other non-clear color
}
return self;
}
First of, I'm not using Storyboard. I tried using UINavigationController+Retro. For some reason, the UINavigationController is having a hard time releasing the UIViewController at the top of the stack. Here's the solution that works for me using iOS 7 custom transition.
Set delegate to self.
navigationController.delegate = self;
Declare this UINavigationControllerDelegate.
- (id<UIViewControllerAnimatedTransitioning>)navigationController (UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
TransitionAnimator *animator = [TransitionAnimator new];
animator.presenting = YES;
return animator;
}
Note that it'll only get called when animated is set to YES. For example
[navigationController pushViewController:currentViewController animated:YES];
Create the animator class extending NSObject. I called mine TransitionAnimator, which was modified from TeehanLax's TLTransitionAnimator inside UIViewController-Transitions-Example.
TransitionAnimator.h
#import <Foundation/Foundation.h>
#interface TransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
#property (nonatomic, assign, getter = isPresenting) BOOL presenting;
#end
TransitionAnimator.m
#import "TransitionAnimator.h"
#implementation TransitionAnimator
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5f;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if (self.presenting) {
//ANIMATE VC ENTERING FROM THE RIGHT SIDE OF THE SCREEN
[transitionContext.containerView addSubview:fromVC.view];
[transitionContext.containerView addSubview:toVC.view];
toVC.view.frame = CGRectMake(0, 0, 2*APP_W0, APP_H0); //SET ORIGINAL POSITION toVC OFF TO THE RIGHT
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
fromVC.view.frame = CGRectMake(0, (-1)*APP_W0, APP_W0, APP_H0); //MOVE fromVC OFF TO THE LEFT
toVC.view.frame = CGRectMake(0, 0, APP_W0, APP_H0); //ANIMATE toVC IN TO OCCUPY THE SCREEN
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}else{
//ANIMATE VC EXITING TO THE RIGHT SIDE OF THE SCREEN
}
}
#end
Use presenting flag to set the direction you want to animate or which ever condition you prefer. Here's the link to Apple reference.
Thanks guys for the feedback. Found a solution in completely recreating the UINavigationController's behavior. When I was nearly finished I ran into Nick Lockwood's solution:
https://github.com/nicklockwood/OSNavigationController
OSNavigationController is a open source re-implementation of UINavigationController. It currently features only a subset of the functionality of UINavigationController, but the long-term aim is to replicate 100% of the features.
OSNavigationController is not really intended to be used as-is. The idea is that you can fork it and then easily customize its appearance and behaviour to suit any special requirements that your app may have. Customizing OSNavigationController is much simpler than trying to customize UINavigationController due to the fact that the code is open and you don't need to worry about private methods, undocumented behavior, or implementation changes between versions.
By overriding my UINavigationController with his code, I was able to work with background images in UINavigationcontrollers
Thanks!
Simply add in:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
This:
[[self window] setBackgroundColor:[UIColor whiteColor]];
The final result:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {
[[self window] setBackgroundColor:[UIColor whiteColor]];
// Override point for customization after application launch.
return YES;
}
Apparently in iOS7 there's a new way define your own custom UIViewController transitions. Look in the docs for UIViewControllerTransitioningDelegate. Also, here's a link to an article about it: http://www.doubleencore.com/2013/09/ios-7-custom-transitions/
Swift 5 implementation of Arne's answer:
extension UINavigationController {
func pushViewControllerLegacyTransition(_ viewController: UIViewController) {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
transition.type = .push
transition.subtype = .fromRight
view.layer.add(transition, forKey: nil)
pushViewController(viewController, animated: false)
}
func popViewControllerLegacyTransition() {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
transition.type = .push
transition.subtype = .fromLeft
view.layer.add(transition, forKey: nil)
popViewController(animated: false)
}
}
Found another great resource to help out:
http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions
Using iOS7 TLTransitionAnimator to create custom animations
I voted for #Arne's answer, because I find it the most elegant solution to this problem. I would just like to add some code in order to answer to #Bill's problem from his comment on #Arne's solution. Here's comment quote:
Thanks, this works for me. However, when the user taps the Back
button, it reverts to the busted animation (because the back button
doesn't call popViewControllerRetro). – Bill Oct 3 at 12:36
In order to call popViewControllerRetro when back button is pressed, there's a small hack you can perform in order to achieve this. Go into your pushed view controller, import UIViewController+Retro.h and add this code in your viewWillDisappear method:
- (void)viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self] == NSNotFound) {
[self.navigationController popViewControllerRetro];
}
[super viewWillDisappear:animated];
}
This if statement will detect when Back button is pressed and will call popViewControllerRetro from category class.
Best regards.
My iOS application supports all orientations except PortraitUpsideDown.
But in the application I have an view with preferences which I want it to only be shown in Portrait orientation. So whenever this view is shown, it is rotated if needed, to be in portrait mode. That means that user will rotate device in portrait mode also, to setup preferences, and then after closing this view interface should now have portrait orientation.
The problem is, that after preferences view is hidden interface stays in landscape orientation, since I block autorotation after this view is shown.
So after the view is hidden I want to manually update the interface to current device orientation. How can I do it?
self.view.hidden=NO;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
self.view.alpha=1.0;
[UIView commitAnimations];
This code is called from the OptionsViewController after a LongPressGesture on its superview.
I created a UIViewController extension to force update of orientation of a controller, based on the solution presented by Marek R. Since the new versions of iOS, his solution does not work anymore. I post here my solution to force orientation update (in order to take into account supported orientation methods of controller) without having side visual effects. Hope it helps.
UIViewController *vc = [[UIViewController alloc]init];
[[vc view] setBackgroundColor:[UIColor clearColor]];
[vc setModalPresentationStyle:(UIModalPresentationOverFullScreen)];
[self presentViewController:vc animated:NO completion:^{
dispatch_async(dispatch_get_main_queue(), ^{
[vc dismissViewControllerAnimated:YES completion:completion];
});
}];
All you have to do is add the following to the view controller you're using for your preferences.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
Calling this workaround works for me:
-(void)forceOrientationUpdate {
UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];
}
Without giving you all my code examples, I'll made this quick.
Has this ever happened to one of you to get viewWillAppear called only the first time it shows up?
I have this problem with all my view.
For example: When my app starts, I get to the StartView which is a main menu. (viewWillAppear gets called) then I press on one button that'll show a navigation controller (viewWillAppear gets called). Then I get back to the main menu (it does not get called) and then I press on the same navigation controller again and it does not get called.
It would be awesome if someone could points me somewhere, I've been searching for this since two days now...
Also if you need more code samples, I can give you some.
For further reading:
That's how I call my navigation controller:
PremierSoinsAppDelegate *AppDelegate = (PremierSoinsAppDelegate *)[[UIApplication sharedApplication] delegate];
UIView *newView = [AppDelegate.navigationController view];
[newView setFrame:CGRectMake(320.0f, 0.0f, 320.0f, 480.0f)];
[UIView beginAnimations:#"RootViewController" context:nil];
[UIView setAnimationDuration:0.3];
newView setFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
UIView commitAnimations];
[AppDelegate.window addSubview:newView];
[AppDelegate.window makeKeyAndVisible];
And that's how I show my menu back:
PremierSoinsAppDelegate *AppDelegate = (PremierSoinsAppDelegate *)[[UIApplication sharedApplication] delegate];
UIView *newView = [AppDelegate.startViewController view];
newView setFrame:CGRectMake(-320.0f, 0.0f, 320.0f, 480.0f)];
UIView beginAnimations:#"StartViewController" context:nil];
UIView setAnimationDuration:0.3];
newView setFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
[UIView commitAnimations];
[AppDelegate.window addSubview:newView];
[AppDelegate.window makeKeyAndVisible];
Thanks A LOT.
You can implement the UINavigationControllerDelegate in your Nav Controller to propagate the viewWillAppear: messages down. You can implement the message like this:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([viewController respondsToSelector:#selector(viewDidAppear:)]) {
[viewController viewDidAppear:animated];
}
}
Note that this is the viewDidAppear and not ViewWillAppear version, but they're basically the same.
However, you should note that the fact that you need to do this may be a sign that something else is wrong in your controller/view code and you might want to reask the question giving us more context to answer it. In particular, I'm assuming that somewhere outside of the code you're giving us, you're pushing and popping view controllers as per usual for a Nav Controller.
viewWill/DidAppear: will only be called when using a UINavigationController or UITabBarController (or really any system-provided-viewControlller managing class) to manipulate views. If you're manually doing this (as you seem to do in your second code snippet, these messages won't get sent.