2

我有一个简单的示例项目,其中我正在玩一些视图控制器自定义动画,以集成到一个更大的项目中,但我发现它们存在一些问题。

我有 2 个简单的视图控制器,第一个带有一个按钮来呈现第二个,带有自定义动画;第二个有一些标签和一个关闭按钮,可以将视图控制器动画“离开场景”

当我呈现视图控制器时,在我单击呈现按钮后,呈现的视图控制器的子视图会在视图控制器动画之前出现在屏幕上,但是当我关闭 VC 时,所有子视图都与被解雇的视图控制器。

在我的视图控制器 2(呈现)上,我有默认自动布局的视图(重置为建议的约束)

我找不到为什么子视图没有像我预期的那样在视图控制器内部动画的理由。

下面是一个显示正在发生的事情的 gif 和源代码:

动图

代码:视图控制器 1(呈现 VC)

import UIKit

class ViewController: UIViewController, UIViewControllerTransitioningDelegate {

    var animator = Animator()

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func presentButton(_ sender: Any) {
        let storyboard = UIStoryboard(name: "Main", bundle: nil);
        let vc = storyboard.instantiateViewController(withIdentifier: "vc2") as! ViewController2
    
        vc.transitioningDelegate = self
        vc.modalPresentationStyle = .custom // chama as funções à parte
    
        present(vc, animated: true, completion: nil)
    }

    // REMARK: UIViewControllerTransitioningDelegate
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        animator.transitioningMode = .Present // sabe que está em presenting mode
        return animator
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        animator.transitioningMode = .Dismiss // Sabe que está em dismissing mode
        return animator
    }

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

}

代码:视图控制器 2(呈现 VC)

import UIKit

class ViewController2: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func dismissButton(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }

}

代码:CustomPresentationController

import UIKit
import Foundation

class CustomPresentationController: UIPresentationController {

    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController!) {
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
    }

    override var frameOfPresentedViewInContainerView: CGRect {
        // arranca a 0
        var presentedViewFrame = CGRect.zero
    
        // Calcula os bounds do container
        let containerBounds = self.containerView?.bounds
    
        // Recalcula o size
        presentedViewFrame.size = CGSize(width: (containerBounds?.size.width)! , height: ((containerBounds?.size.height)! * 0.90))
    
        presentedViewFrame.origin.x = 0
        presentedViewFrame.origin.y = (containerBounds?.size.height)! * 0.1
    
        return presentedViewFrame
    }

}

代码:动画师类

import Foundation
import UIKit

class Animator: NSObject, UIViewControllerAnimatedTransitioning {

    enum Status {
        case Present
        case Dismiss
    }
    var transitioningMode: Status = .Present
    var presentDuration = 1.0
    var dismissDuration = 0.3

    // Tempo da animação
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        if (transitioningMode == .Present) {
            return presentDuration
        } else {
            return dismissDuration
        }
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        // Get the set of relevant objects.
        let containerView = transitionContext.containerView
    
        guard
            let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
            else {
                print("Returning animateTransition VC")
                return
            }
    
        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
        let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)
    
        // Set up some variables for the animation.
        let containerFrame: CGRect = containerView.frame
        var toViewStartFrame: CGRect = transitionContext.initialFrame(for: toVC)
        let toViewFinalFrame: CGRect = transitionContext.finalFrame(for: toVC)
        var fromViewFinalFrame: CGRect = transitionContext.finalFrame(for: fromVC)
    
        // Set up animation parameters.
        if (transitioningMode == .Present) {
            // Modify the frame of the presented view so that it starts
            // offscreen at the lower-right corner of the container.
            toViewStartFrame.origin.x = 0//containerFrame.size.width
            toViewStartFrame.origin.y = containerFrame.size.height * 0.1
        } else {
            // Modify the frame of the dismissed view so it ends in
            // the lower-right corner of the container view.
            fromViewFinalFrame = CGRect(x: containerFrame.size.width,
                                        y: containerFrame.size.height,
                                        width: (toVC.view.frame.size.width),
                                        height: (toVC.view.frame.size.height))
        }
    
        if (transitioningMode == .Present) {
            // Always add the "to" view to the container.
            // And it doesn't hurt to set its start frame.
            containerView.addSubview(toView!)
            toView?.frame = toViewStartFrame
        }
    
        // Animate using the animator's own duration value.
        UIView.animate(withDuration: presentDuration, animations: {
            if (self.transitioningMode == .Present) {
                // Move the presented view into position.
                toView?.frame = toViewFinalFrame
            }
            else {
                // Move the dismissed view offscreen.
                fromView?.frame = fromViewFinalFrame
            }
        }) { (finished) in
            let success = !(transitionContext.transitionWasCancelled)
            // After a failed presentation or successful dismissal, remove the view.
            if ((self.transitioningMode == .Present && !success) || (self.transitioningMode == .Dismiss && success)) {
                toView?.removeFromSuperview()
            }
        
            // Notify UIKit that the transition has finished
            transitionContext.completeTransition(success)
        }
    }

}
4

1 回答 1

4

好吧,我会尽力帮助你。首先,它不工作的原因是自动布局可以定位您的视图,但您的框架基本上为零。为了向您展示这一点,请转到有问题的控制器并让它剪辑其子视图,并且它们不会在过渡期间出现。现在我可能不会更改框架以将其移入,因为您只想将其滑入并且您可能会摆脱一些代码。这就是我的样子。

import UIKit

class ViewController: UIViewController,UIViewControllerTransitioningDelegate {

    var animator = Animator()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func presentModally(_ sender: Any) {
        self.performSegue(withIdentifier: "modal", sender: nil)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "modal"{
           let dvc = segue.destination
            dvc.transitioningDelegate = self
            dvc.modalPresentationStyle = .overCurrentContext
        }
    }

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        animator.transitioningMode = .Present // sabe que está em presenting mode
        return animator
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        animator.transitioningMode = .Dismiss // Sabe que está em dismissing mode
        return animator
    }

}

删除自定义演示控制器。老实说,我认为没有必要。

现在是动画师。

import UIKit

class Animator: NSObject,UIViewControllerAnimatedTransitioning {
    enum Status {
        case Present
        case Dismiss
    }
    var transitioningMode: Status = .Present
    var presentDuration = 1.0
    var dismissDuration = 0.3


    // Tempo da animação
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        if (transitioningMode == .Present) {
            return presentDuration
        } else {
            return dismissDuration
        }
    }


    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        // Get the set of relevant objects.
        let containerView = transitionContext.containerView

        guard
            let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
            else {
                print("Returning animateTransition VC")
                return
        }

        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
        let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)

        // Set up some variables for the animation.
        let containerFrame:     CGRect = containerView.frame
        let toViewFinalFrame:   CGRect = transitionContext.finalFrame(for: toVC)
        var fromViewFinalFrame: CGRect = transitionContext.finalFrame(for: fromVC)

        // Set up animation parameters.
        if (transitioningMode == .Present) {
            let anchor = toViewFinalFrame.origin
            toView?.layer.anchorPoint = anchor
            toView?.layer.position = anchor
            toView?.transform = CGAffineTransform(scaleX: 0, y: 1)
            //another posibility
            //toView?.transform = CGAffineTransform(translationX: -containerView.bounds.width, y: -containerView.bounds.height)
        } else {
            // Modify the frame of the dismissed view so it ends in
            // the lower-right corner of the container view.
            fromViewFinalFrame = CGRect(x: containerFrame.size.width,
                                        y: containerFrame.size.height,
                                        width: (toVC.view.frame.size.width),
                                        height: (toVC.view.frame.size.height))
        }

        if (transitioningMode == .Present) {
            // Always add the "to" view to the container.
            // And it doesn't hurt to set its start frame.
            containerView.addSubview(toView!)
           // toView?.frame = toViewStartFrame
        }

        // Animate using the animator's own duration value.
        UIView.animate(withDuration: presentDuration, animations: {

            if (self.transitioningMode == .Present) {
                // Move the presented view into position.
                toView?.transform = .identity
            }
            else {
                // Move the dismissed view offscreen.
                fromView?.frame = fromViewFinalFrame
            }
        }) { (finished) in
            let success = !(transitionContext.transitionWasCancelled)
            // After a failed presentation or successful dismissal, remove the view.
            if ((self.transitioningMode == .Present && !success) || (self.transitioningMode == .Dismiss && success)) {
                toView?.removeFromSuperview()
            }

            // Notify UIKit that the transition has finished
            transitionContext.completeTransition(success)

        }

    }

}

这就是你所需要的。干杯。

于 2017-03-17T02:54:37.030 回答