UIViewController - 自定义解除转换的问题 [英] UIViewController – issue with custom dismiss transition

查看:92
本文介绍了UIViewController - 自定义解除转换的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个内容UIViewController,它使用自定义转换显示设置UIViewController。演示文稿是 presentViewController:animated:completion:

I have a content UIViewController that presents a settings UIViewController using a custom transition. The presentation is with presentViewController:animated:completion:.

当我稍后用<$ c $解除设置时c> dismissViewControllerAnimated:completion:,演示控制器在设置控制器演示之前突然跳回到它的初始位置。

When I later dismiss the settings with dismissViewControllerAnimated:completion:, the presenting controller is suddenly jumped back to it's initial position prior to the settings controller presentation.

我有一个在设备上解决这个问题但不是模拟器。但是,我想知道我做错了什么而不是黑客攻击它让它消失了。我还打算让这个动画互动,我怀疑这个问题会在我这样做时放大。

I have a work around for this on the device but not the simulator. However, I'd like to know what I'm doing wrong rather than hack in a bodge that makes it go away. I also plan to make this animation interactive, and I suspect this issues will amplify when I do this.

理想的效果是呈现控制器在屏幕上向下滑动,并且呈现的控制器被看作是在它抬起的位置后面填满屏幕。在呈现控制器的使用寿命期间,呈现控制器的顶部保持在屏幕上。它停留在屏幕的底部,但高于所呈现的控制器。

The desired effect is that the presenting controller slides down the screen, and the presented controller is seen to be lying behind it from where it lifts up to fill the screen. The top of the presenting controller remains on-screen during the lifetime of use of the presented controller. It stays at the bottom of the screen, but above the presented controller.

你可以想象在汽车上举起发动机罩(正面呈现)控制器)看到后面的发动机(显示的设置),但发动机罩在底部保持可见的一些背景。

You could imagine lifting the bonnet on a car (the front presenting controller) to see the engine behind (the presented settings), but the bonnet stays visible at the bottom for a bit of context.

我打算对此进行改进,以便呈现控制器似乎真的以3d方式提升视角,但我还没有那么远,但是。

I plan to refine this so that the presenting controller really appears to lift up with perspective in a 3d way, but I've not got that far, yet.

当设置被取消时,原始呈现控制器(发动机罩)应该向上滑回屏幕并且呈现的控制器(设置)稍微下沉(关闭发动机罩)。

When the settings are dismissed, the original presenting controller (bonnet) should slide back up the screen and the presented controller (settings) sink back slightly (closing the bonnet).

这是切换屏幕上和屏幕外设置的方法(它只是由UIButton调用)。你会注意到呈现视图控制器将自己设置为< UIViewControllerTransitioningDelegate>

Here's the method that toggles the settings on and off the screen (it's just called by a UIButton). You'll notice that the presenting view controller sets itself up as the <UIViewControllerTransitioningDelegate>.

-(void) toggleSettingsViewController
{
  const BOOL settingsAreShowing = [self presentedViewController] != nil;
  if(!settingsAreShowing)
  {
    UIViewController *const settingsController = [[self storyboard] instantiateViewControllerWithIdentifier: @"STSettingsViewController"];
    [settingsController setTransitioningDelegate: self];
    [settingsController setModalPresentationStyle: UIModalPresentationCustom];
    [self presentViewController: settingsController animated: YES completion: nil];
  }
  else
  {
    [self dismissViewControllerAnimated: YES completion: nil];
  }
}

实现< UIViewControllerAnimatedTransitioning> ; 呈现视图控制器也只返回< UIViewControllerAnimatedTransitioning>

To implement <UIViewControllerAnimatedTransitioning> the presenting view controller also just returns itself as the <UIViewControllerAnimatedTransitioning>

-(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
  return self;
}

-(id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed
{
  // Test Point 1.
  return self;
}

最后,呈现视图控制器将收到 animateTransition :

So finally, the presenting view controller will receive animateTransition::

-(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
  UIViewController *const fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  UIViewController *const toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

  const BOOL isUnwinding = [toController presentedViewController] == fromController;
  const BOOL isPresenting = !isUnwinding;

  UIViewController * presentingController = isPresenting ? fromController : toController;
  UIViewController * presentedController = isPresenting ? toController : fromController;

  if(isPresenting)
  {
    // Add the presented controller (settings) to the view hierarchy _behind_ the presenting controller.
    [[transitionContext containerView] insertSubview: [presentedController view] belowSubview: [presentingController view]];

    // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
    presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
    presentedController.view.alpha = 0.7;

    [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
      // Lift up the presented controller.
      presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);

      // Brighten the presented controller (out of shadow).
      presentedController.view.alpha = 1;

      // Push the presenting controller down the screen – 3d effect to be added later.
      presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
     } completion: ^(BOOL finished){
       [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
     }];
  }
  else
  {
    // Test Point 2.

    // !!!This line should not be needed!!!
    // It resets the presenting controller to where it ought to be anyway.
    presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);

    [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
      // Bring the presenting controller back to its original position.
      presentingController.view.layer.transform = CATransform3DIdentity;

      // Lower the presented controller again and put it back in to shade.
      presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
      presentedController.view.alpha = 0.4;
    } completion:^(BOOL finished) {
      [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
    }];
  }
}

-(NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
  return 0.5;
}



问题



在上面的代码中,我已经指出 !!!不应该需要此行!!!

正在发生的事情之间是测试点1 测试点2 呈现视图控制器的屏幕位置将重置为默认的全屏界限。因此,它不是在屏幕的底部准备好再次平滑地重新启动动画,而是突然跳到屏幕上以确定它是否也能平滑地进行动画制作!

What's happening is that between Test Point 1 and Test Point 2 the screen position of the presenting view controller is reset to be the default full screen bounds. So, instead of being at the bottom of the screen ready to animate back up again smoothly, it suddenly jumps up the screen to position that it is meant to smoothly animate too!

我尝试了各种方法在屏幕上动画显示视图控制器:

I've tried various approaches to animating the presenting view controller down the screen:


  • 我已经改变了视图的框架。

  • 我已经改变了它的视图转换。

  • 我已经改变了它的视图层的3d变换。

在所有情况下,在测试点1 ,当要求转换委托时,呈现控制器按照我的预期设置。但是,在所有情况下,在测试点2 时,呈现视图控制器已丢失正确位置并已清除以具有我想要为其设置动画的正常全屏位置。

In all cases, at Test Point 1, when the transition delegate is asked for, the presenting controller is set up as I would expect. However, in all cases, at Test Point 2, the presenting view controller has lost the correct position and has been "cleared" to have the normal full screen position that I want to animate it to.

在上面的工作中,我明确地将呈现视图控制器重新定位到动画开始处的位置 !!!不需要此行!!! 即可。这似乎适用于具有当前版本的iOS 7的设备。但是,在模拟器上,控制器在清除位置可见至少一帧。

In the work around above I explicitly relocate the presenting view controller back to where it should be at the start of the animation with !!!This line should not be needed!!!. This seems to work on the device with the current version of iOS 7. However, on the simulator, the controller is visible at the cleared position for at least one frame.

我怀疑我做错了什么,而且我会解决我的解决方法,只是掩盖另一个问题。

I am suspicious that I am doing something else wrong, and that I'm going to get in to trouble with my workaround just masking another problem.

任何想法发生了什么?谢谢!

Any ideas what's going on? Thanks!

推荐答案

使用自定义过渡动画解雇模态呈现的视图控制器的一些潜在问题:

A few potential gotchas with dismissal of modally presented view controllers using custom transition animations:


  • 将呈现的(to)视图添加到容器中,然后将呈现的视图放在前面。不要添加呈现视图,因为您可能会将其从当前超级视图中删除。

  • 在关闭时,UIKit会在调用animateTransition之前将呈现视图的alpha设置为0 。因此,你需要将它设置为1.0或者在关闭你的解雇动画之前完成当前时的任何内容。

  • 同样如此呈现视图的变换。在关闭它之前,它会在调用animateTransition之前重置为identity。

  • Add the presented ("to") view to the container, then bring the presented view front. Don't add the presenting view as you might remove it from its current superview.
  • On dismiss, UIKit sets the alpha of the presented view to 0 before animateTransition is called. So you'll want to set it to 1.0 or whatever it was at the completion of the present before firing off your dismiss animation(s).
  • Likewise for the presented view's transform. On dismiss it gets reset to identity before your animateTransition is called.

鉴于这一切,我认为这应该有效:

Given all that, I think this should work:

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containerView = transitionContext.containerView;

    const BOOL isUnwinding = [toController presentedViewController] == fromController;
    const BOOL isPresenting = !isUnwinding;

    UIViewController *presentingController = isPresenting ? fromController : toController;
    UIViewController *presentedController = isPresenting ? toController : fromController;

    [containerView addSubview:presentingController.view];
    [containerView bringSubviewToFront:presentingController.view];

    if(isPresenting)
    {
        // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
        presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
        presentedController.view.alpha = 0.7;

        [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
            // Lift up the presented controller.
            presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);

            // Brighten the presented controller (out of shadow).
            presentedController.view.alpha = 1;

            // Push the presenting controller down the screen – 3d effect to be added later.
            presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
        } completion: ^(BOOL finished){
            [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
        }];
    }
    else
    {
        presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
        presentedController.view.alpha = 0.7;

        [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
            // Bring the presenting controller back to its original position.
            presentingController.view.layer.transform = CATransform3DIdentity;

            // Lower the presented controller again and put it back in to shade.
            presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
            presentedController.view.alpha = 0.4;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
        }];
    }
}

这篇关于UIViewController - 自定义解除转换的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆