Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/93.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在iOS上的视图之间进行扩展/收缩转换?_Ios_Animation_Uiview_Transition_Modalviewcontroller - Fatal编程技术网

如何在iOS上的视图之间进行扩展/收缩转换?

如何在iOS上的视图之间进行扩展/收缩转换?,ios,animation,uiview,transition,modalviewcontroller,Ios,Animation,Uiview,Transition,Modalviewcontroller,我正在尝试在iOS中制作一个过渡动画,其中视图或视图控制器似乎会展开以填充整个屏幕,然后在完成后收缩回原来的位置。我不确定这种过渡的正式名称,但你可以在iPad的YouTube应用程序中看到一个例子。当您在网格上点击其中一个搜索结果缩略图时,它将从缩略图展开,然后在返回搜索时收缩回缩略图 我对这其中的两个方面感兴趣: 在一个视图和另一个视图之间转换时,您将如何实现这种效果?换句话说,如果视图A占据屏幕的某个区域,您将如何将其转换为占据整个屏幕的视图B,反之亦然 如何以这种方式过渡到模态视图?换句

我正在尝试在iOS中制作一个过渡动画,其中视图或视图控制器似乎会展开以填充整个屏幕,然后在完成后收缩回原来的位置。我不确定这种过渡的正式名称,但你可以在iPad的YouTube应用程序中看到一个例子。当您在网格上点击其中一个搜索结果缩略图时,它将从缩略图展开,然后在返回搜索时收缩回缩略图

我对这其中的两个方面感兴趣:

  • 在一个视图和另一个视图之间转换时,您将如何实现这种效果?换句话说,如果视图A占据屏幕的某个区域,您将如何将其转换为占据整个屏幕的视图B,反之亦然

  • 如何以这种方式过渡到模态视图?换句话说,如果UIViewController C当前正在显示并且包含占据屏幕一部分的视图D,您如何使视图D看起来像是正在转换为UIViewController E,它以模式显示在C的顶部

  • 编辑:我正在增加一笔赏金,看看这是否能让这个问题得到更多的爱

    编辑:我有一些源代码可以做到这一点,Anomie的想法很有魅力,只是做了一些改进。我第一次尝试为模态控制器的视图(E)设置动画,但它没有产生像放大屏幕一样的效果,因为它没有扩展(C)中缩略图视图周围的所有内容。因此,我尝试为原始控制器的视图(C)设置动画,但对其进行重画会产生不稳定的动画,并且像背景纹理这样的东西无法正确缩放。所以,我最后要做的是拍摄原始视图控制器(C)的图像,并在模式视图(E)中放大它。这个方法比我原来的方法要复杂得多,但看起来确实不错!我认为这也是iOS内部转换的方式。无论如何,下面是代码,我在UIViewController上作为一个类别编写

    UIViewController+Transitions.h:

    #import <Foundation/Foundation.h>
    
    @interface UIViewController (Transitions)
    
    // make a transition that looks like a modal view 
    //  is expanding from a subview
    - (void)expandView:(UIView *)sourceView 
            toModalViewController:(UIViewController *)modalViewController;
    
    // make a transition that looks like the current modal view 
    //  is shrinking into a subview
    - (void)dismissModalViewControllerToView:(UIView *)view;
    
    @end
    
    您可以在UIViewController子类中这样使用它:

    #import "UIViewController+Transitions.h"
    
    ...
    
    - (void)userDidTapThumbnail {
    
      DetailViewController *detail = 
        [[DetailViewController alloc]
          initWithNibName:nil bundle:nil];
    
      [self expandView:thumbnailView toModalViewController:detail];
    
      [detail release];
    }
    
    - (void)dismissModalViewControllerAnimated:(BOOL)animated {
      if (([self.modalViewController isKindOfClass:[DetailViewController class]]) &&
          (animated)) {
    
        [self dismissModalViewControllerToView:thumbnailView];
    
      }
      else {
        [super dismissModalViewControllerAnimated:animated];
      }
    }
    
    编辑:事实证明,这并不能真正处理除纵向以外的界面方向。因此,我必须切换到使用视图控制器在UIWindow中设置过渡动画来传递旋转。请参阅下面更复杂的版本:

    UIViewController+Transitions.m:

    #import "UIViewController+Transitions.h"
    
    @implementation UIViewController (Transitions)
    
    // capture a screen-sized image of the receiver
    - (UIImageView *)imageViewFromScreen {
      // make a bitmap copy of the screen
      UIGraphicsBeginImageContextWithOptions(
        [UIScreen mainScreen].bounds.size, YES, 
        [UIScreen mainScreen].scale);
      // get the root layer
      CALayer *layer = self.view.layer;
      while(layer.superlayer) {
        layer = layer.superlayer;
      }
      // render it into the bitmap
      [layer renderInContext:UIGraphicsGetCurrentContext()];
      // get the image
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
      // close the context
      UIGraphicsEndImageContext();
      // make a view for the image
      UIImageView *imageView = 
        [[[UIImageView alloc] initWithImage:image]
          autorelease];
    
      return(imageView);
    }
    
    // make a transform that causes the given subview to fill the screen
    //  (when applied to an image of the screen)
    - (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView {
      // get the root view
      UIView *rootView = sourceView;
      while (rootView.superview) rootView = rootView.superview;
      // convert the source view's center and size into the coordinate
      //  system of the root view
      CGRect sourceRect = [sourceView convertRect:sourceView.bounds toView:rootView];
      CGPoint sourceCenter = CGPointMake(
        CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect));
      CGSize sourceSize = sourceRect.size;
      // get the size and position we're expanding it to
      CGRect screenBounds = [UIScreen mainScreen].bounds;
      CGPoint targetCenter = CGPointMake(
        CGRectGetMidX(screenBounds),
        CGRectGetMidY(screenBounds));
      CGSize targetSize = screenBounds.size;
      // scale so that the view fills the screen
      CATransform3D t = CATransform3DIdentity;
      CGFloat sourceAspect = sourceSize.width / sourceSize.height;
      CGFloat targetAspect = targetSize.width / targetSize.height;
      CGFloat scale = 1.0;
      if (sourceAspect > targetAspect)
        scale = targetSize.width / sourceSize.width;
      else
        scale = targetSize.height / sourceSize.height;
      t = CATransform3DScale(t, scale, scale, 1.0);
      // compensate for the status bar in the screen image
      CGFloat statusBarAdjustment =
        (([UIApplication sharedApplication].statusBarFrame.size.height / 2.0) 
          / scale);
      // transform to center the view
      t = CATransform3DTranslate(t, 
        (targetCenter.x - sourceCenter.x), 
        (targetCenter.y - sourceCenter.y) + statusBarAdjustment, 
        0.0);
    
      return(t);
    }
    
    - (void)expandView:(UIView *)sourceView 
            toModalViewController:(UIViewController *)modalViewController {
    
      // get an image of the screen
      UIImageView *imageView = [self imageViewFromScreen];
    
      // insert it into the modal view's hierarchy
      [self presentModalViewController:modalViewController animated:NO];
      UIView *rootView = modalViewController.view;
      while (rootView.superview) rootView = rootView.superview;
      [rootView addSubview:imageView];
    
      // make a transform that makes the source view fill the screen
      CATransform3D t = [self transformToFillScreenWithSubview:sourceView];
    
      // animate the transform
      [UIView animateWithDuration:0.4
        animations:^(void) {
          imageView.layer.transform = t;
        } completion:^(BOOL finished) {
          [imageView removeFromSuperview];
        }];
    }
    
    - (void)dismissModalViewControllerToView:(UIView *)view {
    
      // take a snapshot of the current screen
      UIImageView *imageView = [self imageViewFromScreen];
    
      // insert it into the root view
      UIView *rootView = self.view;
      while (rootView.superview) rootView = rootView.superview;
      [rootView addSubview:imageView];
    
      // make the subview initially fill the screen
      imageView.layer.transform = [self transformToFillScreenWithSubview:view];
      // remove the modal view
      [self dismissModalViewControllerAnimated:NO];
    
      // animate the screen shrinking back to normal
      [UIView animateWithDuration:0.4 
        animations:^(void) {
          imageView.layer.transform = CATransform3DIdentity;
        }
        completion:^(BOOL finished) {
          [imageView removeFromSuperview];
        }];
    }
    
    @end
    
    @interface ContainerViewController : UIViewController { }
    @end
    
    @implementation ContainerViewController
      - (BOOL)shouldAutorotateToInterfaceOrientation:
              (UIInterfaceOrientation)toInterfaceOrientation {
        return(YES);
      }
    @end
    
    ...
    
    // get the screen size, compensating for orientation
    - (CGSize)screenSize {
      // get the size of the screen (swapping dimensions for other orientations)
      CGSize size = [UIScreen mainScreen].bounds.size;
      if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
        CGFloat width = size.width;
        size.width = size.height;
        size.height = width;
      }
      return(size);
    }
    
    // capture a screen-sized image of the receiver
    - (UIImageView *)imageViewFromScreen {
    
      // get the root layer
      CALayer *layer = self.view.layer;
      while(layer.superlayer) {
        layer = layer.superlayer;
      }
      // get the size of the bitmap
      CGSize size = [self screenSize];
      // make a bitmap to copy the screen into
      UIGraphicsBeginImageContextWithOptions(
        size, YES, 
        [UIScreen mainScreen].scale);
      CGContextRef context = UIGraphicsGetCurrentContext();
      // compensate for orientation
      if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
        CGContextTranslateCTM(context, size.width, 0);
        CGContextRotateCTM(context, M_PI_2);
      }
      else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
        CGContextTranslateCTM(context, 0, size.height);
        CGContextRotateCTM(context, - M_PI_2);
      }
      else if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
        CGContextTranslateCTM(context, size.width, size.height);
        CGContextRotateCTM(context, M_PI);
      }
      // render the layer into the bitmap
      [layer renderInContext:context];
      // get the image
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
      // close the context
      UIGraphicsEndImageContext();
      // make a view for the image
      UIImageView *imageView = 
        [[[UIImageView alloc] initWithImage:image]
          autorelease];
      // done
      return(imageView);
    }
    
    // make a transform that causes the given subview to fill the screen
    //  (when applied to an image of the screen)
    - (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView
                     includeStatusBar:(BOOL)includeStatusBar {
      // get the root view
      UIView *rootView = sourceView;
      while (rootView.superview) rootView = rootView.superview;
      // by default, zoom from the view's bounds
      CGRect sourceRect = sourceView.bounds;
      // convert the source view's center and size into the coordinate
      //  system of the root view
      sourceRect = [sourceView convertRect:sourceRect toView:rootView];
      CGPoint sourceCenter = CGPointMake(
        CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect));
      CGSize sourceSize = sourceRect.size;
      // get the size and position we're expanding it to
      CGSize targetSize = [self screenSize];
      CGPoint targetCenter = CGPointMake(
        targetSize.width / 2.0,
        targetSize.height / 2.0);
    
      // scale so that the view fills the screen
      CATransform3D t = CATransform3DIdentity;
      CGFloat sourceAspect = sourceSize.width / sourceSize.height;
      CGFloat targetAspect = targetSize.width / targetSize.height;
      CGFloat scale = 1.0;
      if (sourceAspect > targetAspect)
        scale = targetSize.width / sourceSize.width;
      else
        scale = targetSize.height / sourceSize.height;
      t = CATransform3DScale(t, scale, scale, 1.0);
      // compensate for the status bar in the screen image
      CGFloat statusBarAdjustment = includeStatusBar ?
        (([UIApplication sharedApplication].statusBarFrame.size.height / 2.0) 
          / scale) : 0.0;
      // transform to center the view
      t = CATransform3DTranslate(t, 
        (targetCenter.x - sourceCenter.x), 
        (targetCenter.y - sourceCenter.y) + statusBarAdjustment, 
        0.0);
    
      return(t);
    }
    
    - (void)expandView:(UIView *)sourceView 
            toModalViewController:(UIViewController *)modalViewController {
    
      // get an image of the screen
      UIImageView *imageView = [self imageViewFromScreen];
      // show the modal view
      [self presentModalViewController:modalViewController animated:NO];
      // make a window to display the transition on top of everything else
      UIWindow *window = 
        [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
      window.hidden = NO;
      window.backgroundColor = [UIColor blackColor];
      // make a view controller to display the image in
      ContainerViewController *vc = [[ContainerViewController alloc] init];
      vc.wantsFullScreenLayout = YES;
      // show the window
      [window setRootViewController:vc];
      [window makeKeyAndVisible];
      // add the image to the window
      [vc.view addSubview:imageView];
    
      // make a transform that makes the source view fill the screen
      CATransform3D t = [self 
        transformToFillScreenWithSubview:sourceView
        includeStatusBar:(! modalViewController.wantsFullScreenLayout)];
    
      // animate the transform
      [UIView animateWithDuration:0.4
        animations:^(void) {
          imageView.layer.transform = t;
        } completion:^(BOOL finished) {
          // we're going to crossfade, so change the background to clear
          window.backgroundColor = [UIColor clearColor];
          // do a little crossfade
          [UIView animateWithDuration:0.25 
            animations:^(void) {
              imageView.alpha = 0.0;
            }
            completion:^(BOOL finished) {
              window.hidden = YES;
              [window release];
              [vc release];
            }];
        }];
    }
    
    - (void)dismissModalViewControllerToView:(UIView *)view {
    
      // temporarily remove the modal dialog so we can get an accurate screenshot 
      //  with orientation applied
      UIViewController *modalViewController = [self.modalViewController retain];
      [self dismissModalViewControllerAnimated:NO];
    
      // capture the screen
      UIImageView *imageView = [self imageViewFromScreen];
      // put the modal view controller back
      [self presentModalViewController:modalViewController animated:NO];
      [modalViewController release];
    
      // make a window to display the transition on top of everything else
      UIWindow *window = 
        [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
      window.hidden = NO;
      window.backgroundColor = [UIColor clearColor];
      // make a view controller to display the image in
      ContainerViewController *vc = [[ContainerViewController alloc] init];
      vc.wantsFullScreenLayout = YES;
      // show the window
      [window setRootViewController:vc];
      [window makeKeyAndVisible];
      // add the image to the window
      [vc.view addSubview:imageView];
    
      // make the subview initially fill the screen
      imageView.layer.transform = [self 
        transformToFillScreenWithSubview:view
        includeStatusBar:(! self.modalViewController.wantsFullScreenLayout)];
    
      // animate a little crossfade
      imageView.alpha = 0.0;
      [UIView animateWithDuration:0.15 
        animations:^(void) {
          imageView.alpha = 1.0;
        }
        completion:^(BOOL finished) {
          // remove the modal view
          [self dismissModalViewControllerAnimated:NO];
          // set the background so the real screen won't show through
          window.backgroundColor = [UIColor blackColor];
          // animate the screen shrinking back to normal
          [UIView animateWithDuration:0.4 
            animations:^(void) {
              imageView.layer.transform = CATransform3DIdentity;
            }
            completion:^(BOOL finished) {
              // hide the transition stuff
              window.hidden = YES;
              [window release];
              [vc release];
            }];
        }];
    
    }
    
    唷!但现在它看起来就像苹果的版本,没有使用任何受限的API。此外,即使在模态视图位于前方时方向发生变化,它也可以工作

  • 制作效果很简单。获取全尺寸视图,初始化其
    变换
    中心
    以将其定位在缩略图顶部,将其添加到相应的superview,然后在动画块中重置变换和
    中心
    ,以将其定位在最终位置。要关闭视图,只需执行相反的操作:在动画块中设置
    transform
    center
    以将其放置在缩略图的顶部,然后在完成块中将其完全删除

    请注意,尝试从一个点(即宽度为0、高度为0的矩形)进行缩放会使事情变得一团糟。如果您想这样做,可以从宽度/高度为0.00001左右的矩形进行缩放

  • 一种方法是执行与#1中相同的操作,然后调用
    presentModalViewController:animated:
    ,在动画完成时显示实际的视图控制器(如果操作正确,将不会由于
    presentModalViewController:animated:
    调用而导致任何可见的差异)。和
    dismissModalViewControllerAnimated:
    不带后跟与#1中相同的字符以解除

    或者,您可以像#1中那样直接操纵模态视图控制器的视图,并接受
    parentViewController
    interface-orientation
    ,以及其他一些东西在模态视图控制器中无法正常工作,因为苹果不支持我们创建自己的容器视图控制器


  • 在观看了Youtube iPad动画后,我发现这只是一个幻觉。假设搜索结果有一个
    SearchViewController
    ,视频本身有一个
    DetailViewController
    ,以及视频的附加信息

    DetailViewController有一个类似于
    -(id)initWithFullscreen
    的方法,它使用视频的全屏空间启动视图控制器

    所以顺序是这样的:

  • SearchViewController显示其结果
  • 用户点击视频
  • DetailViewController是使用
    initWithFullscreen
    创建的,但未显示
  • “放大”动画开始。(请注意,我们仍然使用SearchViewController,而此动画只是一个简单的视图动画)
  • “放大”动画结束,向DetailViewController显示
    动画:NO
    (如前所述)
  • 现在显示了DetailViewController,并使用了全部空间

  • youtube应用程序似乎没有做任何更有趣的事情,但它的好处是“放大”动画在显示完整视频之前会放大到一个黑色方块。

    谢谢。我不清楚我们在第二部分将要制作什么动画。在模态视图控制器的视图以模态方式呈现之前,是否可以对其设置动画?在这种情况下,它必须添加为当前显示的视图控制器的子视图,对吗?我很有兴趣看一些这方面的源代码。@JesseCrossen:在#2中的第一种方法,您将为一些视图设置动画,这些视图看起来像模态视图控制器的视图。可能只是使用CALayer的
    RenderContext:
    创建的“屏幕截图”。第二种方法是操纵视图控制器的vi