iOS自定义过渡动画

时间:2017-05-23 12:19:37

标签: ios swift uiviewanimation uiviewanimationtransition

我已经开始使用UIViewControllerAnimatedTransitioning协议学习自定义过渡动画了。我们在youtube上找到的所有视频都是基于流程的,当我们有新的ViewController呈现圆形动画或类似的时候。

我在实现转换方式时遇到问题。大多数情况下,我需要的是类似于 facebook app 以及它们如何打开全屏图像查看器。

因此,我们说 VC1VC2。在VC1,我们将行动称为VC2。在VC两个上我们都有相同的UI元素。在我的情况下是UIImageView。与您一样,点击imageView上的VC1,它会打开某个对象的详细信息页面,其顶部是图片。我希望有动画,看起来像VC1的图像正在将帧更改为VC2的最终图像帧,然后详细页面上的其他内容(如标签,按钮等)应该出现。

但我在训练期间遇到了一些问题 1.首先,我不理解containerView transitionContext的想法。但正如我所看到的,它就像转换之间的中间状态。那是对的吗?但这很奇怪 对我来说,因为即使backgroundColor财产也不适用于containerView 2.我不明白在转换过程中我需要做什么动画,以及containerView子视图的结构应该是什么。在我的例子中,当我提出VC2时,我需要隐藏其所有子视图。然后将VC1的imageView动画显示为imageView的{​​{1}}帧,然后再次显示所有子视图。那么,在这种情况下,VC2应该添加到containerView?如果是,那么它应该是来自imageView的实际imageView,还是具有相同帧/图像的imageView的全新副本,只是在转换期间临时使用...

将我链接到带有类似动画的examples / tutorial / code

会很有帮助

Here is link to how that works in facebook

2 个答案:

答案 0 :(得分:10)

了解自定义过渡动画

如果你从VCA导航到VCB那么

  1. 首先,您需要使用UIViewControllerTransitioningDelegate
  2. 转换委托负责提供用于自定义转换的动画控制器。您指定的委托对象必须符合UIViewControllerTransitioningDelegate协议。

    1. 现在您必须使用UIViewControllerAnimatedTransitioning
    2. 它负责动画视图的持续时间实际逻辑的转换。

      这些代表的工作方式与您在两个VC's之间的工作方式相同。

      要成功完成转换,您必须执行以下步骤:

      1. 因此,首先要使用它

        • 设置modalPresentationStyle = .custom
        • 指定transitonDelegate财产。
      2. func animateTransition(_ : )中你必须使用上下文containerView,因为你在两个VC's之间,所以你需要任何容器,你可以做任何动画,所以上下文为你提供那个你可以做动画的容器。

      3. 现在您需要fromView& toViewVCA.view& VCB.view resp。现在在containerView中添加这两个视图并编写动画的核心逻辑。

      4. 最后要注意的是在转换上下文对象上调用的completeTransition(_ :)方法。动画完成后,必须调用此方法,让系统知道视图控制器已完成转换。

      5. 这是过渡动画的核心基础。

        我不知道FB动画,所以我只是解释了你的其余问题。

        <强>参考

        您可以提出任何进一步的信息。

          

        代码添加

        关于图像选择

        添加VC_A

        var selectedImage: UIImageView?
         let transition = PopAnimator()
        
          override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
                super.viewWillTransition(to: size, with: coordinator)
        
                coordinator.animate(
                  alongsideTransition: {context in
                    self.bgImage.alpha = (size.width>size.height) ? 0.25 : 0.55
                    self.positionListItems()
                  },
                  completion: nil
                )
              }
        //position all images inside the list
          func positionListItems() {
            let listHeight = listView.frame.height
            let itemHeight: CGFloat = listHeight * 1.33
            let aspectRatio = UIScreen.main.bounds.height / UIScreen.main.bounds.width
            let itemWidth: CGFloat = itemHeight / aspectRatio
        
            let horizontalPadding: CGFloat = 10.0
        
            for i in herbs.indices {
              let imageView = listView.viewWithTag(i) as! UIImageView
              imageView.frame = CGRect(
                x: CGFloat(i) * itemWidth + CGFloat(i+1) * horizontalPadding, y: 0.0,
                width: itemWidth, height: itemHeight)
            }
        
            listView.contentSize = CGSize(
              width: CGFloat(herbs.count) * (itemWidth + horizontalPadding) + horizontalPadding,
              height:  0)
          }
        
        // On image selection
        VC_B.transitioningDelegate = self
            present(VC_B, animated: true, completion: nil)
        
        
        
           // add extension
        extension VC_A: UIViewControllerTransitioningDelegate {
        
          func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            transition.originFrame = selectedImage!.superview!.convert(selectedImage!.frame, to: nil)
        
            transition.presenting = true
            selectedImage!.isHidden = true
        
            return transition
          }
        
          func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            transition.presenting = false
            return transition
          }
        }
        

        和动画类

        class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {
        
          let duration = 1.0
          var presenting = true
          var originFrame = CGRect.zero
        
          var dismissCompletion: (()->Void)?
        
          func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return duration
          }
        
          func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            let containerView = transitionContext.containerView
        
            let toView = transitionContext.view(forKey: .to)!
        
            let herbView = presenting ? toView : transitionContext.view(forKey: .from)!
        
            let initialFrame = presenting ? originFrame : herbView.frame
            let finalFrame = presenting ? herbView.frame : originFrame
        
            let xScaleFactor = presenting ?
        
              initialFrame.width / finalFrame.width :
              finalFrame.width / initialFrame.width
        
            let yScaleFactor = presenting ?
        
              initialFrame.height / finalFrame.height :
              finalFrame.height / initialFrame.height
        
            let scaleTransform = CGAffineTransform(scaleX: xScaleFactor, y: yScaleFactor)
        
            if presenting {
              herbView.transform = scaleTransform
              herbView.center = CGPoint(
                x: initialFrame.midX,
                y: initialFrame.midY)
              herbView.clipsToBounds = true
            }
        
            containerView.addSubview(toView)
            containerView.bringSubview(toFront: herbView)
        
            UIView.animate(withDuration: duration, delay:0.0, usingSpringWithDamping: 0.4,
              initialSpringVelocity: 0.0,
              animations: {
                herbView.transform = self.presenting ?
                  CGAffineTransform.identity : scaleTransform
                herbView.center = CGPoint(x: finalFrame.midX,
                                          y: finalFrame.midY)
              },
              completion:{_ in
                if !self.presenting {
                  self.dismissCompletion?()
                }
                transitionContext.completeTransition(true)
              }
            )
          }
        
        }
        

        输出

        enter image description here

        Git-hub Repo: https://github.com/thedahiyaboy/TDCustomTransitions

        • xcode:9.2

        • swift:4

答案 1 :(得分:1)

UIViewControllerAnimatedTransitioning的核心方法是animateTransition。我在尝试解释基本想法时添加了评论。

let duration = 0.5
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    // Works like an empty scratchpad/slate.
    // This is the view that will be shown on screen when animation starts and upto 
    // it ends.
    // Any animations done here are visible to user.
    // Nothing right now, in this container(*).
    let containerView = transitionContext.containerView

    // Grab the controller to animate from and to.
    let fromView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!.view
    let toView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!.view

    // We might need some other view's that need to be animated, like the UIImageView
    // In  your case this image view must exists on fromView and also in toView
    let fromImageView = UIImageView() // We should get image from fromController
    let toImageView = UIImageView() // We should get image from toController

    // Since the containerView has no views as of now(*), we need to add our fromView first
    containerView.addSubview(fromView!)

    // We will also add, the to view but with alpha 0 so that is not visible initially
    toView?.alpha = 0.0
    // Add this to view to container
    containerView.addSubview(toView!)


    UIView.animate(withDuration: duration, animations: { 
        // We do animations here, something like,
        fromImageView.frame = (toView?.frame)! // With some checking around the view relative frames
        toView?.alpha = 1.0
    }) { (completed) in
        // Do clean up here, after this completeTransition(true) method,
        // the comtainer will be removed from the screen and toView will be shown automatically
        transitionContext.completeTransition(true)
    }
}