【问题标题】:My subviews doesn't animate with the view controller我的子视图不使用视图控制器进行动画处理
【发布时间】:2017-03-17 00:58:29
【问题描述】:

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

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

}

【问题讨论】:

  • 蓝色视图是否应该位于呈现视图控制器的顶部下方。我知道问题所在,但如果我要显示代码,我希望它适合您的用例。有导航栏什么的
  • 这是在当前上下文中吗?
  • 动画效果(从哪里来,从哪里结束并不重要,以后改)
  • 是的,在当前上下文中:)

标签: ios animation swift3 uimodalpresentationcustom


【解决方案1】:

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

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)

        }

    }

}

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

【讨论】:

  • 我认为如果我将来想更改视图控制器的大小,那么自定义表示类是必需的。我会试试你写的,我会尽快反馈。感谢您的帮助:)
  • 您可以保留自定义演示控制器,但我会返回您希望框架的确切大小,并使用平移或缩放将其移动到位。只是我的2美分。当我制作动画并且实际上要更改帧时,我通常会拍摄快照并隐藏原始图像,最后取消隐藏并删除快照。但我只为视图或子视图的快照设置动画。这样我就永远不会真正操纵真实视图的框架。希望这也有帮助。
  • 不错的提示。你看过 iOS 10.3 的 mail.app 的新动画了吗?当您创建新电子邮件时,视图控制器来自底部,但没有到达顶部,它就像是 90% 的大小,背景视图控制器稍微扩大了一点,您可以看到它们。这就是我要在这里创建的 ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 2020-04-20
相关资源
最近更新 更多