0

I'm trying to provide a generic implementation of UIViewControllerAnimatedTransitioning using Swift protocols, but whenever I have an object which conforms to the protocol, I get the error:

Command failed due to signal: Segmentation fault: 11

When the generics are removed, I still get the problem.

Here's my protocol, with the generics commented out:

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)
}

PresentingViewController and PresentedViewController would just be in place of UIViewController in prepareViewController(::), presentViewController(::), and dismissViewController(::).

I provide the implementation for UIViewControllerAnimatedTransitioning in an extension for TransitionControllerType:

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) { }
}

Trying to run the project at this point will result in a successful build.

If I attempt to implement the protocol on an NSObject (for conformance to UIViewControllerAnimatedTransitioning), I get the segmentation fault error.

Below is the class that I use for the transitioningDelegate when presenting AddNewMenuViewController, and the TransitionControllerType used by

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()
    }
}

Why does the non-generic version result in the same segmentation fault as the generic version?

HighFlyingFantasy
  • 3,789
  • 2
  • 26
  • 38

1 Answers1

0

I don't think the compiler recognizes the default method implementation of animateTransition added by extension to the TransitionControllerType protocol when trying to figure if your AddNewMenuTransitionController class satisfies the UIViewControllerAnimatedTransitioning protocol (which is required by your declaration of the TransitionControllerType).

That may be a bit much to ask of the compiler since it would essentially need to pre-build the method list with the default implementation of TransitionControllerType while it is verifying availability of methods for one of its requirements.

This may work (but I haven't tried it) if you don't require UIViewControllerAnimatedTransitioning in the definition of TransitionControllerType but add it explicitly to your class definition instead.

I'm sure this is not as clean as what you were aiming for but the indirect default implementation of a method using an intermediate protocol seems to be too much for the compiler at this time.

Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • I removed conformance to `UIVIewControllerAnimatedTransitioning` from `TransitionControllerType`, and added it to my `AddNewMenuTransitionController`. Unfortunately, there's still the seg fault. Any other ideas? – HighFlyingFantasy Jan 12 '16 at 16:30
  • I've also tried to add `extension UIViewControllerAnimatedTransitioning where Self: TransitionControllerType`, but all of the methods must be marked `@objc`, but you cannot mark methods `@objc` in an extension – HighFlyingFantasy Jan 12 '16 at 16:32
  • I don't know if using a protocol is required by some other part of your application but you could always make TransitionControllerType an NSObject derived class and derive AddNewMenuTransitionController from it instead of NSObject (and do away with the protocol altogether) – Alain T. Jan 13 '16 at 02:52
  • ^That's what I did. I'm going to post the solution shortly – HighFlyingFantasy Jan 14 '16 at 13:40