【问题标题】:Push Segue animation without UINavigationController在没有 UINavigationController 的情况下推送 Segue 动画
【发布时间】:2013-06-29 00:21:49
【问题描述】:

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

(没有将我的控制器转换为使用 UINavigationController)

到目前为止找到的答案适用于导航控制器,但在不使用导航控制器时会失败。 (我仍在阅读并尝试在堆栈溢出中看到的其他答案)

感谢 2012 年 7 月 5 日 (ZHCustomSegue.m) Zakir,我的自定义 segue .m

#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];
}

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

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

【问题讨论】:

    标签: ios uiviewcontroller uistoryboardsegue catransition


    【解决方案1】:

    由于您正在调用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];
                         }];
    }
    

    【讨论】:

      【解决方案2】:

      看来我上面的代码“几乎”可以工作,但并不完全正常。 手头的主要问题可能是何时调用最后一步。

      我的工作代码如下所示:

      #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

      【讨论】:

      • 如果有人可以将其改写为使用完成块,我希望更好,但这可行。
      【解决方案3】:

      我已经合并了两个答案的想法以获得此代码(运行良好): (例如,上面的答案不计入设备的方向,因此它们不能在横向上工作)

      (带有 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
      */
      

      【讨论】:

        猜你喜欢
        • 2016-02-23
        • 1970-01-01
        • 2018-10-12
        • 2015-10-15
        • 1970-01-01
        • 2013-04-18
        • 1970-01-01
        • 2012-01-13
        相关资源
        最近更新 更多