ios - 从未调用过的交互式委托(delegate)方法

标签 ios swift delegates transition interactive

我想在 ViewController (1) 和 NavigationViewController (2) 之间进行交互式转换。

NavigationController 由按钮调用,因此在呈现时没有交互式过渡。它可以通过按钮或 UIPanGestureRecognizer 关闭,因此可以交互地关闭或不关闭。

我有一个名为 TransitionManager 的对象,用于转换,是 UIPercentDrivenInteractiveTransition 的子类。

下面代码的问题在于两个委托(delegate)方法 interactionControllerFor... 从未被调用。

此外,当我按下按钮或滑动 (UIPanGestureRecognizer) 时,模态转场的基本动画就完成了。所以这两个委托(delegate)方法 animationControllerFor... 也不起作用。

有什么想法吗?谢谢

ViewController.swift

let transitionManager = TransitionManager()

override func viewDidLoad() {
    super.viewDidLoad()

    self.transitioningDelegate = transitionManager
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        let dest = segue.destinationViewController as UIViewController
        dest.transitioningDelegate = transitionManager
        dest.modalPresentationStyle = .Custom
}

TransitionManager.swift

class TransitionPushManager: UIPercentDrivenInteractiveTransition,
 UINavigationControllerDelegate, UIViewControllerTransitioningDelegate {


@IBOutlet var navigationController: UINavigationController!

var animation : Animator! // Implement UIViewControllerAnimatedTransitioning protocol


override func awakeFromNib() {
    var panGesture = UIPanGestureRecognizer(target: self, action: "gestureHandler:")
    navigationController.view.addGestureRecognizer(panGesture)

    animation = Animator()
}

func gestureHandler(pan : UIPanGestureRecognizer) {

    switch pan.state {

    case .Began :

        interactive = true

            navigationController.presentingViewController?.dismissViewControllerAnimated(true, completion:nil)


    case .Changed :

        ...            

    default :

        ...

        interactive = false

    }

}


func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return animation
}

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return animation
}

func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    return nil
}

func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    return self.interactive ? self : nil
}

主 Storyboard

  • ViewController 上的按钮触发了呈现 NavigationController 的模态转场

  • NavigationController 的委托(delegate)导出链接到 TransitionManager 类的对象

  • NavigationController 在 TransitionManager 类中由属性“navigationController”引用

最佳答案

我认为关键问题是您在 viewDidLoad 中配置 transitionDelegate。这在这个过程中往往为时已晚。您应该在 init 导航 Controller 时执行此操作。

让我们想象一下您的根场景(“Root”)呈现导航 Controller 场景(“Nav”),然后从场景 A 推送到 B 再到 C,例如,我想像这样的对象模型,其中导航 Controller 将简单地拥有自己的动画 Controller 、交互 Controller 和手势识别器:

view controller hierarchy and object model

这就是您考虑 (a) 当“root”显示“nav”时的自定义转换(非交互); (b) 当“nav”为了返回到“root”而自行解散时的自定义转换(交互或非交互)。所以,我将导航 Controller 子类化为:

  • 在其 View 中添加手势识别器;

  • 设置 transitioningDelegate 以在您从根场景过渡到导航 Controller 场景(以及返回)时生成自定义动画:

  • transitioningDelegate 还将返回交互 Controller (仅在手势识别器进行时存在),在手势期间产生交互式转换,如果您在外部关闭则产生非交互式转换手势的上下文。

在 Swift 3 中,它看起来像:

import UIKit
import UIKit.UIGestureRecognizerSubclass

class CustomNavigationController: UINavigationController {
    
    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }
    
    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)
        configure()
    }
    
    private func configure() {
        transitioningDelegate = self   // for presenting the original navigation controller
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        delegate = self                // for navigation controller custom transitions
        
        let left = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleSwipeFromLeft(_:)))
        left.edges = .left
        view.addGestureRecognizer(left)
    }
    
    fileprivate var interactionController: UIPercentDrivenInteractiveTransition?
    
    func handleSwipeFromLeft(_ gesture: UIScreenEdgePanGestureRecognizer) {
        let percent = gesture.translation(in: gesture.view!).x / gesture.view!.bounds.size.width
        
        if gesture.state == .began {
            interactionController = UIPercentDrivenInteractiveTransition()
            if viewControllers.count > 1 {
                popViewController(animated: true)
            } else {
                dismiss(animated: true)
            }
        } else if gesture.state == .changed {
            interactionController?.update(percent)
        } else if gesture.state == .ended {
            if percent > 0.5 && gesture.state != .cancelled {
                interactionController?.finish()
            } else {
                interactionController?.cancel()
            }
            interactionController = nil
        }
    }
}

// MARK: - UINavigationControllerDelegate
//
// Use this for custom transitions as you push/pop between the various child view controllers 
// of the navigation controller. If you don't need a custom animation there, you can comment this
// out.

extension CustomNavigationController: UINavigationControllerDelegate {
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        
        if operation == .push {
            return ForwardAnimator()
        } else if operation == .pop {
            return BackAnimator()
        }
        return nil
    }
    
    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactionController
    }
    
}

// MARK: - UIViewControllerTransitioningDelegate
//
// This is needed for the animation when we initially present the navigation controller. 
// If you're only looking for custom animations as you push/pop between the child view
// controllers of the navigation controller, this is not needed. This is only for the 
// custom transition of the initial `present` and `dismiss` of the navigation controller 
// itself.

extension CustomNavigationController: UIViewControllerTransitioningDelegate {

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return ForwardAnimator()
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return BackAnimator()
    }

    func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactionController
    }

    func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactionController
    }

    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return PresentationController(presentedViewController: presented, presenting: presenting)
    }

}

// When doing custom `present`/`dismiss` that overlays the entire
// screen, you generally want to remove the presenting view controller's
// view from the view hierarchy. This presentation controller
// subclass accomplishes that for us.

class PresentationController: UIPresentationController {
    override var shouldRemovePresentersView: Bool { return true }
}

// You can do whatever you want in the animation; I'm just fading

class ForwardAnimator : NSObject, UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    func animateTransition(using context: UIViewControllerContextTransitioning) {
        let toView = context.viewController(forKey: .to)!.view!
        
        context.containerView.addSubview(toView)
        
        toView.alpha = 0.0
        
        UIView.animate(withDuration: transitionDuration(using: context), animations: {
            toView.alpha = 1.0
        }, completion: { finished in
            context.completeTransition(!context.transitionWasCancelled)
        })
    }
    
}

class BackAnimator : NSObject, UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    func animateTransition(using context: UIViewControllerContextTransitioning) {
        let toView   = context.viewController(forKey: .to)!.view!
        let fromView = context.viewController(forKey: .from)!.view!
        
        context.containerView.insertSubview(toView, belowSubview: fromView)
        
        UIView.animate(withDuration: transitionDuration(using: context), animations: {
            fromView.alpha = 0.0
        }, completion: { finished in
            context.completeTransition(!context.transitionWasCancelled)
        })
    }
}

所以,我可以将 Storyboard中导航 Controller 的基类更改为这个自定义子类,现在根场景可以只显示导航 Controller (没有特殊的 prepare(for:)),一切正常。

关于ios - 从未调用过的交互式委托(delegate)方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26680311/

相关文章:

ios - Xcode 6 归档并在将 "Skipping copy phase strip ,binary is code signed"添加到目标时收到警告 "share extension"

ios - CapturePhoto 无法快速工作 3

swift - 无法将 type() 的值转换为 Swift 2 中的闭包结果类型 NSDictionary

ios - 单点触控/iOS : which place is the best one to unsubscribe the delegate

objective-c - NSTextField 自动完成方法

ios - 最佳实践 : Using Size Classes for Single table view to dual table view

ios - 无法在 viewDidLoad : 中以编程方式更改 constraint.constant

swift - Kitura 的 session 路线?

ios - Swift:如何递增以获取集合的图像

c# - 尝试设置委托(delegate)和处理程序以仅使用操作键动态调用