Transition from one UIViewController to another in a custom segue - objective-c

This custom segue visually works correctly but there is an warning when completed.
Warning: Attempt to present <DestViewController: 0x21059f40> on <SrcViewController: 0x1fd50cf0> whose view is not in the window hierarchy!
Any help on how to get this to work without the warning would be much appreciated, have spent more time on this than i should. Not even sure if I should care about it since it is a warning.
- (void)perform {
UIViewController *src = (UIViewController *)self.sourceViewController;
UIViewController *dst = (UIViewController *)self.destinationViewController;
dst.view.alpha = 0;
[UIView animateWithDuration:0.5
animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[src.view addSubview:dst.view];
dst.view.alpha = 1;
}
completion:^(BOOL finished){
[dst.view removeFromSuperview];
//need to understand why this throws a warning but still works
[src presentViewController:dst animated:NO completion:^(){
[src reset];
}];
}];
}
UPDATE
There was a call to stop a MPMoviePlayerController in the reset method on the src UIViewController. For some reason that was causing the issue, once removed this segue works perfectly. See my answer below for final implementation of segue.

You are removing the view from superview i.e src.view and presenting the same viewController after it , so it is showing warning.
Try this (If this doesn't work tell me):
[UIView animateWithDuration:0.5
animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
dst.view.alpha = 1;
}
completion:^(BOOL finished){
[src presentViewController:dst animated:NO completion:^(){
[src reset];
}];
}];
}

although the original issue was caused by something outside of the segue, as mentioned above, i thought i would post the full working segue code for others who may end up here. The delegate in this example is a custom UIViewController container where the child UIViewContainers where added as children in the following way:
[self addChildViewController:childController_];
[self.view addSubview:childController_.view];
segue.h
#import <UIKit/UIKit.h>
#interface COFSimpleSegue : UIStoryboardSegue
#property (assign) UIViewController *delegate;
#end
segue.m
#import "COFSimpleSegue.h"
#import <QuartzCore/QuartzCore.h>
#implementation COFSimpleSegue
#synthesize delegate = delegate_;
- (void)perform {
UIViewController *src = (UIViewController *)self.sourceViewController;
UIViewController *dst = (UIViewController *)self.destinationViewController;
dst.view.frame = delegate_.view.bounds;
dst.view.autoresizingMask = delegate_.view.autoresizingMask;
[src willMoveToParentViewController:nil];
[delegate_
transitionFromViewController:src
toViewController:dst
duration:0.5f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^(void){}
completion:^(BOOL finished) {
[dst didMoveToParentViewController:delegate_];
[src removeFromParentViewController];
}
];
}
#end

Related

How Popup Keyboard After Segue to New Naviagtion Controller Animation Has Finished

I segue from a tableview to detail view controller using custom segues.
#implementation QuickNoteFlipSegue
- (void) perform {
UIViewController *src = (UIViewController *) self.sourceViewController;
UIViewController *dst = (UIViewController *) self.destinationViewController;
dst.navigationItem.hidesBackButton = YES;
[UIView transitionWithView:src.navigationController.view duration:1.00
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
[src.navigationController pushViewController:dst animated:NO];
}
completion:^(BOOL finished){
if (finished) {
}
}];
}
#end
What I would like to do it when the transition has finished, popup the keyboard for my textview.
I currently do this by calling
[textView becomeFirstResponder];
This pops up the keyboard OK but before the transition animation has finished. How to detect when the animation has finished before I popup the keyboard?
I should maybe put something is the completion of the animation, but how to make the custom segue aware of the textview in it's destination?
Do:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[textView becomeFirstResponder];
}
Add a method to your DestinationViewController:
-(void)openKeyboard
{
[textView becomeFirstResponder];
}
Then change your perform method:
- (void) perform {
UIViewController *src = (UIViewController *) self.sourceViewController;
DestinationViewController *dst = (DestinationViewController *) self.destinationViewController;
dst.navigationItem.hidesBackButton = YES;
[UIView transitionWithView:src.navigationController.view duration:1.00
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
[src.navigationController pushViewController:dst animated:NO];
}
completion:^(BOOL finished){
if (finished) {
[dst openKeyboard];
}
}];
}

How to change speed of segue between ViewControllers

Is it possible to control segue speed?
I have checked the documentation but there is no given method by Apple.
But I am more looking for ideas to hack in and change lower level code to make segueing in slow motion.
Below code is of custom segue, and you can set duration of transition in code.
- (void)perform
{
UIViewController *src = (UIViewController *) self.sourceViewController;
UIViewController *dst = (UIViewController *) self.destinationViewController;
[UIView transitionFromView:src.view
toView:dst.view
duration:3.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:NULL];
}
Sample project is here in GitHub:https://github.com/weed/p120805_CustomSegue
You can download and just run it. I wish this is help for you.
to prevent zombies creation i think better to do use subview add/remove as well:
- (void)perform
{
UIView *src = ((UIViewController *) self.sourceViewController).view;
UIView *dst = ((UIViewController *) self.destinationViewController).view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
[window insertSubview:dst aboveSubview:src];
[UIView transitionFromView:src
toView:dst
duration:1.5
options:UIViewAnimationOptionTransitionCrossDissolve
completion:^(BOOL finished){
[src removeFromSuperview];
window.rootViewController = self.destinationViewController;
}];
}
this is if you are not using navigation controller!

How would I implement a splash screen that animates away?

This is what I have so far:
#synthesize window = _window;
#synthesize tabBarController = _tabBarController;
-(void)animate
{
[UIView animateWithDuration:1 animations:^{
splashImage.frame = CGRectMake(0,480,320,480);
}];
[UIView commitAnimations];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
splashImage = [[UIImageView alloc] initWithImage:[UIImage
imageNamed:#"carolinaWolf.png"]];
splashImage.frame = CGRectMake(0, 0, 320, 480);
[self.tabBarController.view addSubview:splashImage];
[self animate];
return YES;
}
My initial view in storyboard is a TabBarController. What I want to happen is: after I finish loading new content into the application, I want to splash screen to animate down and off the screen. This worked before I started using storyboard, why won't it work now?
My AppDelegate.h looks like this:
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UITabBarController *tabBarController;
#end
UIImageView *splashImage;
You can directly just add your splashImage to self.window.rootViewController like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIImage *splashImage = [UIImage autoAdjustImageNamed:#"Default.png"];
UIImageView *splashImageView = [[UIImageView alloc] initWithImage:splashImage];
[self.window.rootViewController.view addSubview:splashImageView];
[self.window.rootViewController.view bringSubviewToFront:splashImageView];
[UIView animateWithDuration:1.5f
delay:2.0f
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
splashImageView.alpha = .0f;
CGFloat x = -60.0f;
CGFloat y = -120.0f;
splashImageView.frame = CGRectMake(x,
y,
splashImageView.frame.size.width-2*x,
splashImageView.frame.size.height-2*y);
} completion:^(BOOL finished){
if (finished) {
[splashImageView removeFromSuperview];
}
}];
return YES;
}
This is how I fade away my splash screens:
#pragma mark Fading Default.png Animation
#interface FadeDefault : UIViewController
#end
#implementation FadeDefault
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView *fader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default#2x.png"]];
[fader setFrame:CGRectMake(0,0,320,480)];
/*if ( IDIOM == IPAD ) /* Optional */
{
fader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default-Portrait~ipad.png"]];
[fader setFrame:CGRectMake(0,0,768,1024)];
}else
{
fader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default#2x.png"]];
[fader setFrame:CGRectMake(0,0,320,480)];
}*/
self.view = fader;
[fader release];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(imageDidFadeOut:finished:context:)];
self.view.alpha = 0.0;
[UIView commitAnimations];
}
- (void)imageDidFadeOut:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
[self.view removeFromSuperview];
}
#end
#pragma mark - Application lifecycle
Keep in mind this is an example, although code I actually use and I know works, you need to keep your other didFinishLaunchingWithOptions code in place. Just add the fader code to you existing code.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
/* custom fading Default.png animation */
FadeDefault * fader = [[FadeDefault alloc] init];
[window addSubview:navigationController.view];
[window addSubview:fader.view];
[fader release];
[window makeKeyAndVisible];
return YES;
}

Animation between two UIViewController when I do makeKeyAndVisible

I'm trying to do an animation to switch between two UIViewController that I have. But when the animation runs, the old UIViewController is white.
// viewController is the new UIViewController
[UIView animateWithDuration:0.6
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^(void){
[viewController.view setTransform:CGAffineTransformMakeTranslation(0, -460)];
}
completion:^(BOOL finished){
if (finished){
//
}
}
];
[[[[UIApplication sharedApplication] delegate] window] setRootViewController:viewController];
[[[[UIApplication sharedApplication] delegate] window] makeKeyAndVisible];
I have an object which always has the UIViewController:
#interface ManagerViews : NSObject {
UIViewController *oneController;
UIViewController *twoController;
...
}
#property(nonatomic, retain) UIViewController *oneController;
#property(nonatomic, retain) UIViewController *twoController;
...
What am I doing wrong?
Have you tried to set the old UIViewController to "nil" or (my Objective-C expertise is a little rusty) reset?

iAd Banner View Delegate Not Calling Methods

I have an iAd banner view, with all my contracts up and running, and I've implemented the ADBannerView delegate. The banner should disappear with no internet connection, but it just shows a white box where the content should be. I know I have all the code right, I've seen a million tutorials on this. So I ran some tests and found that the banner view wasn't even calling the two methods for the delegate! Here is the code.
In the .h file:
#import <iAd/iAd.h>
#interface DetailViewController : ADBannerViewDelegate>
{
ADBannerView *aBanner;
BOOL bannerIsVisible;
}
#property (nonatomic, retain) IBOutlet ADBannerView *aBanner;
#property (nonatomic, assign) BOOL bannerIsVisible;
#end
in the .m file:
#implementation DetailViewController
#synthesize aBanner,bannerIsVisible;
//Show banner if can load ad.
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if (!self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOn" context:NULL]; banner.frame = CGRectOffset(banner.frame, 0, -banner.frame.size.height);
[UIView commitAnimations]; self.bannerIsVisible = YES; }
}
//Hide banner if can't load ad.
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOff" context:NULL]; banner.frame = CGRectOffset(banner.frame, 0, banner.frame.size.height);
[UIView commitAnimations]; self.bannerIsVisible = NO; }
}
You have to set the banner delegate to files owner. I had the same issue and after beating my head against the wall it was that simple.
Use the code shown below to declare your own delegate
-(void)viewDidLoad {
aBanner.delegate = self;
}