扩展 UIViewControllerAnimatedTransitioning 时出现分段错误

Posted

技术标签:

【中文标题】扩展 UIViewControllerAnimatedTransitioning 时出现分段错误【英文标题】:Segmentation fault when extending UIViewControllerAnimatedTransitioning 【发布时间】:2016-01-11 23:16:48 【问题描述】:

我正在尝试使用 Swift 协议提供UIViewControllerAnimatedTransitioning 的通用实现,但是每当我有一个符合该协议的对象时,我都会收到错误消息:

Command failed due to signal: Segmentation fault: 11

删除泛型后,我仍然遇到问题。

这是我的协议,泛型已被注释掉:

protocol TransitionControllerType: UIViewControllerAnimatedTransitioning 
//    typealias PresentingViewController: UIViewController
//    typealias PresentedViewController: UIViewController

    var isPresentation: Bool  get set 
    var presentationTransitionDuration: NSTimeInterval  get 
    var dismissTransitionDuration: NSTimeInterval  get 

    func prepareViewControllerForPresentation(viewController: UIViewController, presentingViewController: UIViewController)
    func presentViewController(viewController: UIViewController, presentingViewController: UIViewController)
    func dismissViewController(viewController: UIViewController, presentingViewController: UIViewController)

PresentingViewControllerPresentedViewController 将在 prepareViewController(::)presentViewController(::)dismissViewController(::) 中代替 UIViewController

我在TransitionControllerType 的扩展中提供UIViewControllerAnimatedTransitioning 的实现:

extension TransitionControllerType 
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval 
        return isPresentation ? presentationTransitionDuration : dismissTransitionDuration
    

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) 
        // Ensure there is a container view
        guard let containerView = transitionContext.containerView() else 
            return
        

        // Get the view controllers
        let (fromViewController, toViewController) = transitionContext.viewControllers()

//        // Cast the view controllers
//        guard let presentedViewController = (isPresentation ? toViewController : fromViewController) as? PresentedViewController,
//            let presentingViewController = (isPresentation ? fromViewController : toViewController) as? PresentingViewController
//        else 
//            return
//        
        guard let presentedViewController = (isPresentation ? toViewController : fromViewController),
            let presentingViewController = (isPresentation ? fromViewController : toViewController)
        else 
            return
        

        // Get the views from the view controllers
        let presentedView = presentedViewController.view
        let presentingView = presentingViewController.view

        // If it's a presentation, prepare the view controllers
        if isPresentation 
            prepareViewControllerForPresentation(presentedViewController, presentingViewController: presentingViewController)
            containerView.addSubview(presentedView)
        

        UIView.animateWithDuration(
            transitionDuration(transitionContext),
            delay: 0,
            usingSpringWithDamping: isPresentation ? PresentationSpringDamping : DismissSpringDamping,
            initialSpringVelocity: isPresentation ? PresentationSpringVelocity : DismissSpringVelocity,
            options: isPresentation ? UIViewAnimationOptions.CurveEaseOut : [],
            animations: 
                if self.isPresentation 
                    self.presentViewController(presentedViewController, presentingViewController: presentingViewController)
                 else 
                    self.dismissViewController(presentedViewController, presentingViewController: presentingViewController)
                
            ,
            completion:  success in
                transitionContext.completeTransition(success)

                // !!!: We have to manually add `presentationView` due to a bug
                // http://openradar.appspot.com/radar?id=5320103646199808
                if !self.isPresentation 
                    UIApplication.sharedApplication().keyWindow?.addSubview(presentingView)
                 else 
                    UIApplication.sharedApplication().keyWindow?.addSubview(presentedView)
                
        )
    


    func prepareViewControllerForPresentation(viewController: UIViewController, presentingViewController: UIViewController)  
    func presentViewController(viewController: UIViewController, presentingViewController: UIViewController)  
    func dismissViewController(viewController: UIViewController, presentingViewController: UIViewController)  

此时尝试运行项目将导致构建成功。

如果我尝试在 NSObject 上实现协议(为了符合 UIViewControllerAnimatedTransitioning),我会收到分段错误错误。

以下是我在呈现AddNewMenuViewController 时用于transitioningDelegate 的类,以及

使用的TransitionControllerType
class AddNewMenuTransitionControllerDelegate: NSObject, UIViewControllerTransitioningDelegate 
    func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? 
        return AddNewMenuPresentationController(presentedViewController: presented, presentingViewController: presenting, blurStyle: .Dark)
    

    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? 
        return AddNewMenuTransitionController(presentation: true)
    

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? 
        return AddNewMenuTransitionController()
    


class AddNewMenuTransitionController: NSObject, TransitionControllerType 
    var isPresentation: Bool
    var presentationTransitionDuration: NSTimeInterval = 0.3
    var dismissTransitionDuration: NSTimeInterval = 0.3

    init(presentation: Bool = false) 
        self.isPresentation = presentation

        super.init()
    

为什么非泛型版本会导致与泛型版本相同的分段错误?

【问题讨论】:

【参考方案1】:

当试图确定您的 AddNewMenuTransitionController 类是否满足 UIViewControllerAnimatedTransitioning 协议(您的 TransitionControllerType 声明所要求的)时,我认为编译器无法识别通过扩展添加到 TransitionControllerType 协议的 animateTransition 的默认方法实现。

这对编译器的要求可能有点高,因为它本质上需要使用 TransitionControllerType 的默认实现预先构建方法列表,同时它正在验证方法的可用性以满足其要求之一。

如果您在 TransitionControllerType 的定义中不需要 UIViewControllerAnimatedTransitioning 而是将其显式添加到您的类定义中,这可能有效(但我没有尝试过)。

我确信这不像您的目标那样干净,但是使用中间协议的方法的间接默认实现目前对于编译器来说似乎太多了。

【讨论】:

我从TransitionControllerType 中删除了与UIVIewControllerAnimatedTransitioning 的一致性,并将其添加到我的AddNewMenuTransitionController 中。不幸的是,仍然存在段错误。还有其他想法吗? 我也试过添加extension UIViewControllerAnimatedTransitioning where Self: TransitionControllerType,但是所有方法都必须标记@objc,但是不能在扩展中标记方法@objc 我不知道您的应用程序的其他部分是否需要使用协议,但您始终可以将 TransitionControllerType 设为 NSObject 派生类并从中派生 AddNewMenuTransitionController 而不是 NSObject(并取消协议) ^这就是我所做的。我将很快发布解决方案

以上是关于扩展 UIViewControllerAnimatedTransitioning 时出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章

GroovyGroovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )

003-正则的扩展数值的扩展函数的扩展数组的扩展对象的扩展

GroovyGroovy 扩展方法 ( 实例扩展方法配置 | 扩展方法示例 | 编译实例扩展类 | 打包实例扩展类字节码到 jar 包中 | 测试使用 Thread 实例扩展方法 )

GroovyGroovy 扩展方法 ( 静态扩展方法配置 | 扩展方法示例 | 编译静态扩展类 | 打包静态扩展类字节码到 jar 包中 | 测试使用 Thread 静态扩展类 )

Kotlin扩展函数 ③ ( 定义扩展文件 | 重命名扩展函数 | Kotlin 标准库扩展函数 )

Kotlin扩展函数 ② ( 扩展属性 | 为可空类型定义扩展函数 | 使用 infix 关键字修饰单个参数扩展函数的简略写法 )