使用自定义转场执行自定义转换的正确方法 - 没有副作用

Posted

技术标签:

【中文标题】使用自定义转场执行自定义转换的正确方法 - 没有副作用【英文标题】:Proper way of performing custom transitions with custom segues - WITHOUT SIDE EFFECTS 【发布时间】:2014-02-05 15:31:39 【问题描述】:

考虑到网上有很多教程向您展示如何使用自定义 UIStoryboardSegue 在视图控制器之间进行自定义动画转换,我想知道是否有“正确”的方法来做到这一点。

我应该注意,我正在研究如何以模态方式执行此操作,而不是使用导航控制器。

我意识到有一些新方法是专门为在 ios 7 中为视图控制器之间的过渡制作动画而量身定制的,但是如果我需要针对 iOS 6 怎么办?或者,如果 iOS 7 的做法存在缺陷(确实存在,请参阅我的 SO 问题 here),而使用自定义 segue 方法很容易解决。

问题是大多数教程都是这样的:

在您的自定义 segue perform 方法中,您将首先将一个视图控制器的视图添加到另一个视图控制器,然后执行您想要的任何自定义动画。然后在动画完成后,您将调用(如果以模态方式呈现或关闭)

[sourceViewController presentViewController:destinationViewController animated:NO completion:NULL]

这种方法的问题在于 viewWillAppear: 和 viewDidAppear: 被调用了两次,而 viewWillDisappear 等被调用了一次,都在过渡动画中。这不是我们想要的行为。

有没有适当的方法可以实现这一点?有关此问题的进一步讨论,请参阅 Phil Beauvoir 在其博客上发布的 tutorial 并向下滚动至 cmets。

此外,我发现其他人也问过几乎相同的问题,但我仍在问我的问题,因为我的研究发现了他的question 中没有解决的几个方面。此外,在他的问题上给出的答案并没有解决这个问题,而是提供了另一种方法,即 iOS 7 方法,它不适用于我的特殊需要。

【问题讨论】:

这个问题有点宽泛且不具体。我不知道有“一种正确的方法”来进行过渡,您是否正在尝试完成特定的过渡?您试图避免的副作用是视图外观回调的不正确数量(和时间?),还是还有更多? @rdelmar 没有不尊重的意思,但这个问题既不广泛也不具体。如果您通读问题并查看链接,您会看到执行自定义动画的所有建议方法(动画本身无关紧要 - 只需知道它是自定义的),您会发现它们存在缺陷并且有副作用.我相信 Phil Beauvoir 的教程是关于该主题的更简单但更全面的教程之一,通过遵循它并测试他的示例,您可以看到存在不需要的副作用。那么,再一次,有没有合适的方法??? @rdelmar 是的,副作用是视图外观回调被调用的次数不正确(有些调用确实不应该——我在看着你 viewWillDisappear:)这些都可以解决,但自定义 segue 的执行方式显然有问题。 Apple 自己的 segue 不会导致这些副作用。 我已阅读您的所有链接,但我的意思是,可能没有一种正确的方法可以做到这一点。例如,当使用导航控制器时,你有一个容器控制器,但你没有模态表示。这些可能需要不同的方法来避免副作用。 @rdelmar 我明白你的意思。我将编辑我的答案,专门用于模态演示。 【参考方案1】:

我认为没有理想的方法可以做到这一点。正如您链接到的其他问题的其中一个 cmets 中所提到的,Apple 没有让我们访问复制其模式转换所需的一切。具体来说,presentingViewController 和presentedViewController 属性是只读的,所以我们不能设置它们。对视图回调的过多调用的问题是将调用 presentViewController:animated:completion: 放在过渡的完成块中,因此消除该调用可以解决大部分特定问题。

我最接近复制 Apple 的模态转换(但从顶部)是这样做:

#import "CoverVerticalDown.h"
#import "ViewController.h"
#import "BlueViewController.h"

@implementation CoverVerticalDown

-(void)perform 
    ViewController *s = self.sourceViewController;
    BlueViewController *d = self.destinationViewController;
    [s.view.window addSubview: d.view];
    d.view.frame = CGRectMake(0, -480, 320, 480);
    [UIView animateWithDuration:.5 animations:^
        d.view.frame = CGRectMake(0, 0, 320, 480);
     completion:^(BOOL finished) 
        NSLog(@"Starting completion block of transition");
        [s.view removeFromSuperview];
        s.rdPresentedViewController = d;
        d.rdPresentingViewcontroller = s;
    ];

这确实使最终状态与模态转换相同——即窗口只有一个视图(新视图),窗口的 rootViewController 仍然是源视图控制器,并且没有子视图控制器。但是正如您所看到的,要做到这一点,segue 需要“知道”(通过导入)源和目标控制器类是什么,所以我可以设置它们的自定义 rdPresentedViewController 和 rdPresentingViewController 属性。至少需要设置其中的第一个,以便保留呈现的控制器(我猜这可以在 prepareForSegue 中完成,所以也许你不必在 segue 代码中使用它)。与标准模态的回调序列有一个区别——源控制器的 viewWillDisappear 方法直到完成块才被调用,而它在正常模态的转换期间被调用。这种方法,也没有考虑旋转,所以如果你旋转设备然后做过渡,它会从侧面进来。要解决这个问题,您必须自己进行旋转变换。

【讨论】:

我认为还有另一个时间问题——目标控制器的 viewWillAppear 和 viewDidAppear 方法都在转换期间被调用。所以,这些回调的数量是正确的,并且它们的顺序是正确的(我认为),但时间仍然有点偏差。 所以,暂时扮演魔鬼的拥护者,除了它是解决问题的一种 hacky 方法,你必须小心你在外观方法中执行的代码vc 的,如果您只是使用我在问题中链接的 Phil 编写的教程,您还会遇到哪些其他潜在问题? @daveMac,我不确定,因为我没有尝试过他的代码,但我认为您的主要问题是过多的回调,该方法无法修复,对吗?你真正需要什么行为?您可以更改我上面的代码,将目标控制器添加为子 viewController,它设置 childViewController 和 parentViewController 属性,您可以使用它们来代替 presenting 和 presentingViewController 属性(并保留子视图)。您还可以将子视图添加到源而不是窗口,从而为您提供正确的旋转行为(但视图不会消失)。 过多的回调是我的问题,我只是想知道您是否知道这可能导致的任何问题,而不是显而易见的问题。

以上是关于使用自定义转场执行自定义转换的正确方法 - 没有副作用的主要内容,如果未能解决你的问题,请参考以下文章

没有segue的自定义关闭过渡

如何使用情节提要从自定义 uitableviewcell 正确启动弹出框转场

自定义Controller转场动画

Android的Fragment的自定义转场动画

swift自定义转场动画(比较有难度)

自定义 Viewcontroller 转换无法正确调整大小