在没有UINavigationController的情况下推送Segue动画

时间:2013-06-29 00:21:49

标签: ios uiviewcontroller uistoryboardsegue catransition

我正在尝试使用自定义segue在UIViewControllers之间添加一个简单的推送动画。

(不转换我的控制器以使用UINavigationController)

到目前为止找到的答案与导航控制器一起工作正常,但在不使用导航控制器时失败。 (我仍在阅读并尝试其他我在堆栈溢出时看到的答案)

我的定制segue .m感谢(ZHCustomSegue.m)Zakir于7/5/12

#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"

@implementation SFCustomSegue


-(void)perform {

UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];

CATransition* transition = [CATransition animation];
transition.duration = 4.0;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;

[sourceViewController.view.layer addAnimation:transition forKey:kCATransition];

[sourceViewController presentViewController:[self destinationViewController] animated:NO completion:nil];

}

如果我按照Zakir的原始示例使用导航控制器,并将最后两行替换为:

    [sourceViewController.navigationController.view.layer addAnimation:transition
                                                            forKey:kCATransition];

[sourceViewController.navigationController pushViewController:destinationController animated:NO];    

它有效......

Apple的医生说(注意不是导航控制器,并使用模态segue):

- (void)perform
{
// Add your own animation code here.

[[self sourceViewController] presentModalViewController:[self destinationViewController] animated:NO];
}

通过NOT Working,我没有动画。在Zakir的代码中,使用4.0秒需要4.0秒,我得到了我想要的动画。当然,如果我使用导航控制器,推送转换是默认的。在这段代码中,我没有动画,它需要0秒。

有没有人使用自定义segue动画来使用UIViewControllers?

3 个答案:

答案 0 :(得分:2)

由于您正在调用presentViewController,因此源视图控制器仍然存在。如果您确实想要使用目标视图控制器替换源视图控制器,以便取消分配源,则可以执行以下操作:

static UIImageView *screenShotOfView(UIView *view)
{
    // Create a snapshot for animation
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [view.layer renderInContext:context];
    UIImageView *screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
    UIGraphicsEndImageContext();
    return screenShot;
}

- (void)perform
{
    UIViewController *source = (UIViewController *) self.sourceViewController;
    UIViewController *destination = (UIViewController *) self.destinationViewController;

    // Swap the snapshot out for the source view controller
    UIWindow *window = source.view.window;
    UIImageView *screenShot = screenShotOfView(source.view);
    CGRect originalFrame = destination.view.frame;

    BOOL animsEnabled = [UIView areAnimationsEnabled];
    [UIView setAnimationsEnabled:NO];
    {
        window.rootViewController = destination;
        [window addSubview:screenShot];
        [source.view removeFromSuperview];
        CGRect frame = destination.view.frame;
        frame.origin.x += source.view.bounds.size.width;
        destination.view.frame = frame;
    }
    [UIView setAnimationsEnabled:animsEnabled];

    [UIView animateWithDuration:kAnimationDuration
                     animations:^{
                         destination.view.frame = originalFrame;
                         CGRect frame = screenShot.frame;
                         frame.origin.x -= screenShot.bounds.size.width;
                         screenShot.frame = frame;

                     }
                     completion:^(BOOL finished) {
                         [screenShot removeFromSuperview];
                     }];
}

答案 1 :(得分:0)

看来我上面的代码“几乎”正在工作,但并不完全。 当调用最后一步时,可能是

我的工作代码如下:

#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"

@implementation SFCustomSegue

static const float delay = 0.5f;

-(void)perform {

    UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
    UIViewController *destinationController = (UIViewController*)[self destinationViewController];

    [sourceViewController.view addSubview:destinationController.view];

    CATransition* transition = [CATransition animation];
    transition.duration = delay;
    transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionPush;
    transition.subtype = kCATransitionFromRight;

    [sourceViewController.view.layer addAnimation:transition forKey:kCATransition];

    [self performSelector:@selector(performDone:) withObject:destinationController afterDelay:delay];
}

- (void)performDone:(id)viewController{
    UIViewController *destination = (UIViewController*)viewController;
    [destination.view removeFromSuperview];
    [[self sourceViewController] presentViewController:destination animated:NO completion:nil];

}

@end

解决此问题的关键是添加转换视图(第13行),然后将其删除(第28行)并使用 presentViewController:animated:completion:将其添加回(第29行)。

我仍然只使用常规的CATransition转换类型“卡住”,但这对我来说已经足够了,因为我想使用 kCATransitionPush

答案 2 :(得分:0)

我已将两个答案中的想法合并,以获得此代码(工作正常): (上面的答案不计入设备的方向,因此它们不会在横向上工作,例如)

(带有CATransitions的变体不合适,因为两个视图之间存在间隙而另一个视图推动了另一个视图)

#import "PushSegue.h"

/*
@interface TransitionDelegate : NSObject
- (id) initWithScreenshot: (UIView*) screenshot;
@end
*/

@implementation PushSegue
- (void) perform
{
    UIViewController* source = (UIViewController*) self.sourceViewController;
    UIViewController* destination = (UIViewController*) self.destinationViewController;

    const BOOL iPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
    float animationDuration = iPad ? 0.5 : 0.3;

    // Swap the snapshot out for the source view controller
    UIWindow* window = source.view.window;
    UIImageView* screenShot = screenShotOfView(source.view);

    // accord to device orientation

    float rotation;
    //NSString* transitionType;

    CGRect originalFrame = destination.view.frame;
    CGRect destinationFrame = destination.view.frame;

    switch ([UIApplication sharedApplication].statusBarOrientation)
    {
        case UIInterfaceOrientationPortraitUpsideDown:
            rotation = M_PI;
            destinationFrame.origin.x -= source.view.bounds.size.width;
            //transitionType = kCATransitionFromLeft;
            break;

        case UIInterfaceOrientationLandscapeLeft:
            rotation = M_PI + M_PI_2;
            destinationFrame.origin.y -= source.view.bounds.size.width;
            //transitionType = kCATransitionFromBottom;
            break;

        case UIInterfaceOrientationLandscapeRight:
            rotation = M_PI_2;
            destinationFrame.origin.y += source.view.bounds.size.width;
            //transitionType = kCATransitionFromTop;
            break;

        default:
            rotation = 0;
            destinationFrame.origin.x += source.view.bounds.size.width;
            //transitionType = kCATransitionFromRight;
            break;
    }

    screenShot.transform = CGAffineTransformMakeRotation(rotation);

    // reposition after rotation
    CGRect screenshotFrame = screenShot.frame;
    screenshotFrame.origin.x = 0;
    screenshotFrame.origin.y = 0;
    screenShot.frame = screenshotFrame;

    switch ([UIApplication sharedApplication].statusBarOrientation)
    {
        case UIInterfaceOrientationPortraitUpsideDown:
            screenshotFrame.origin.x += screenShot.bounds.size.width;
            break;

        case UIInterfaceOrientationLandscapeLeft:
            screenshotFrame.origin.y += screenShot.bounds.size.width;
            break;

        case UIInterfaceOrientationLandscapeRight:
            screenshotFrame.origin.y -= screenShot.bounds.size.width;
            break;

        default:
            screenshotFrame.origin.x -= screenShot.bounds.size.width;
            break;
    }

    // swap the view with its screenshot
    window.rootViewController = destination;
    [window addSubview:screenShot];
    [source.view removeFromSuperview];

    /*
    CATransition* transition = [CATransition animation];

    transition.duration = animationDuration;
    transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionPush;
    transition.subtype = transitionType;
    transition.delegate = [[TransitionDelegate alloc] initWithScreenshot:screenShot];

    [window addSubview:destination.view];

    [screenShot.window.layer addAnimation:transition forKey:nil];
    */

    const BOOL animationsEnabled = [UIView areAnimationsEnabled];
    [UIView setAnimationsEnabled:NO];
    {
        destination.view.frame = destinationFrame;
    }
    [UIView setAnimationsEnabled:animationsEnabled];

    [UIView animateWithDuration:animationDuration
                     animations:^
    {
        destination.view.frame = originalFrame;
        screenShot.frame = screenshotFrame;
    }
                     completion:^(BOOL finished)
    {
        [screenShot removeFromSuperview];
    }];

    /*
    const BOOL animationsEnabled = [UIView areAnimationsEnabled];
    [UIView setAnimationsEnabled:NO];
    {
        CGRect frame = destination.view.frame;
        frame.origin.x += source.view.bounds.size.width;
        destination.view.frame = frame;
    }
    [UIView setAnimationsEnabled:animationsEnabled];

    [UIView animateWithDuration:animationDuration
                     animations:^
    {
        destination.view.frame = originalFrame;
        CGRect frame = screenShot.frame;
        frame.origin.x -= screenShot.bounds.size.width;
        screenShot.frame = frame;
    }
    completion:^(BOOL finished)
    {
        [screenShot removeFromSuperview];
    }];
    */

    /*
    CATransition* transition = [CATransition animation];

    transition.duration = animationDuration;
    transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionPush;

    switch ([UIApplication sharedApplication].statusBarOrientation)
    {
        case UIInterfaceOrientationPortraitUpsideDown:
            transition.subtype = kCATransitionFromLeft;
            break;

        case UIInterfaceOrientationLandscapeLeft:
            transition.subtype = kCATransitionFromBottom;
            break;

        case UIInterfaceOrientationLandscapeRight:
            transition.subtype = kCATransitionFromTop;
            break;

        default:
            transition.subtype = kCATransitionFromRight;
            break;
    }

    [source.view.window.layer addAnimation:transition forKey:nil];

    [source presentViewController:destination animated:NO completion:nil];
    */
}

static UIImageView* screenShotOfView(UIView* view)
{
    // Create a snapshot for animation
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    [view.layer renderInContext:context];
    UIImageView* screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];

    UIGraphicsEndImageContext();

    return screenShot;
}
@end

/*
@implementation TransitionDelegate
{
    UIView* screenshot;
}

- (id) initWithScreenshot: (UIView*) screenshot
{
    if (self = [super init])
    {
        self->screenshot = screenshot;
    }
    return self;
}

- (void) animationDidStop: (CAAnimation*) theAnimation
                 finished: (BOOL) flag
{
    [screenshot removeFromSuperview];
}
@end
*/