动画 CALayer

Posted

技术标签:

【中文标题】动画 CALayer【英文标题】:animating a CALayer 【发布时间】:2017-06-19 03:07:38 【问题描述】:

我在 UIView 之上有一个 CALayer。

UIView 的动画效果完全符合我的要求,但其顶部的 PlayerView 似乎在屏幕外“生成”然后动画到 UIView 在 然后 开始的位置 它将动画到 UIViews 框架。

我想知道 CALayers 是否有任何属性可以自动处理这个问题,但我没有找到任何东西。

下面是代码:

    let window = UIApplication.shared.keyWindow!
     v = UIView(frame: CGRect(x: window.frame.origin.x, y: window.frame.origin.y, width: window.frame.width, height: window.frame.height))
     let v2 = UIView(frame: .zero)
    window.addSubview(v!);
    window.addSubview(v2)
    v?.backgroundColor = UIColor.black
    v2.backgroundColor = UIColor.clear

    v?.layer.cornerRadius = (v?.frame.width)! * 0.025






    guard let path = Bundle.main.path(forResource: "video", ofType:"mp4") else 
        debugPrint("video.m4v not found")
        return
    

    player = AVPlayer(url: URL(fileURLWithPath: path))

    playerLayer = AVPlayerLayer(player: player)
    //        playerLayer.frame = playerView.frame

    playerLayer?.frame = (playerView?.bounds)!
    playerLayer?.masksToBounds = true

    playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill


    v?.layer.addSublayer(playerLayer!)

    player?.play()
    player?.isMuted = true

    //looper
    NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: nil)
     notification in
        let t1 = CMTimeMake(5, 100)
        self.player?.seek(to: t1)

        self.player?.play()
    







    let superview = playerView?.superview

    v2.frame = (v2.convert((playerView?.frame)!, from: superview))
            v?.frame = (v?.convert((playerView?.frame)!, from: superview))!

    window.addConstraintsWithFormat("H:|[v0]|", views: v!)
    window.addConstraintsWithFormat("V:|[v0]|", views: v!)

            playerLayer?.frame = (v?.frame)!



                self.playerLayer?.frame = (self.v?.frame)!


    UIView.animate(withDuration: 4, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: 



        self.window?.layoutIfNeeded()

    , completion:  (completed) in


        self.playerLayer?.frame = (self.v?.frame)!



    )



有什么建议吗?

编辑:

我已经包含了更新的代码

    let window = UIApplication.shared.keyWindow!
    v = UIView(frame: CGRect(x: window.frame.origin.x, y: window.frame.origin.y, width: window.frame.width, height: window.frame.height))
    let v2 = UIView(frame: .zero)
    window.addSubview(v!);

    v?.addSubview(playerView2!)

    window.addSubview(v2)
    v?.backgroundColor = UIColor.black
    v2.backgroundColor = UIColor.clear

    v?.layer.cornerRadius = (v?.frame.width)! * 0.025










    playerView2?.layer.cornerRadius = (playerView2?.bounds.width)! * 0.025

    guard let path = Bundle.main.path(forResource: "video", ofType:"mp4") else 
        debugPrint("video.m4v not found")
        return
    

    player = AVPlayer(url: URL(fileURLWithPath: path))

    playerLayer = AVPlayerLayer(player: player)
    //        playerLayer.frame = playerView.frame

    playerLayer?.frame = (playerView2?.bounds)!
    playerLayer?.masksToBounds = true
    playerLayer?.cornerRadius = (playerView2?.bounds.width)! * 0.025

    playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill


    self.playerView2?.layer.addSublayer(playerLayer!)

//        player?.play()
    player?.isMuted = true

    //looper
    NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: nil)
     notification in
        let t1 = CMTimeMake(5, 100)
        self.player?.seek(to: t1)

//            self.player?.play()
    










    self.playerView2?.frame = (self.v?.bounds)!
    self.playerLayer?.frame = (self.playerView?.bounds)!





    UIView.animate(withDuration: 5, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: 



        self.v?.frame = window.frame

        self.playerView2?.frame = (self.v?.frame)!

        self.playerLayer?.frame = (self.playerView2?.frame)!



        self.window?.layoutIfNeeded()

    , completion:  (completed) in




    )






【问题讨论】:

【参考方案1】:

当您设置playerLayer.frame 时,您将获得一个隐式动画。

如果您不想要任何动画,请暂时禁用隐式动画,如下所示:

        UIView.animate(withDuration: 4, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: 
            self.window?.layoutIfNeeded()
        , completion:  (completed) in
            let priorValue = CATransaction.disableActions()
            CATransaction.setDisableActions(true)
            self.playerLayer?.frame = self.v!.frame
            CATransaction.setDisableActions(priorValue)
        )

如果您确实想要为playerLayer.frame 设置动画,那么最好的方法是创建一个使用AVPlayerLayer 作为其层的UIView 子类,如下所示:

class PlayerView: UIView 
    override class var layerClass: AnyClass  return AVPlayerLayer.self 
    private(set) lazy var playerLayer: AVPlayerLayer =  self.layer as! AVPlayerLayer ()

然后您可以使用PlayerView 而不是裸露的AVPlayerLayer 并使用UIKit 动画。由于您不能直接初始化AVPlayerLayerUIView 使用默认初始化程序来创建其层),您需要像这样设置PlayerView 的播放器:

        playerView?.playerLayer = player

【讨论】:

我能够使用您的第二个建议并将 UIView 与 AVPlayer 一起使用,但是,UIView 和 PlayerLayer 仍然不能一致地制作动画。每当我尝试强制动画时,playerLayer 都会正确地掩蔽到 UIView,但这是一个尴尬的跳跃,而不是平滑的动画。还有什么建议吗? 我需要查看您修改后的代码,并更好地描述您不喜欢的情况。 它有点工作,只是 playerLayer 没有与 v 和 playerView 一起动画......另外两个动画完美地一致。当前的行为就像self.playerLayer?.frame = (self.playerView2?.frame)! 行在其他两个开始动画后读取一两秒,因此它跳到帧而不是平滑地为过渡设置动画。

以上是关于动画 CALayer的主要内容,如果未能解决你的问题,请参考以下文章

图层CALayer

iOS基础05—-UIView与CALayer的联系与区别

iOS基础05—-UIView与CALayer的联系与区别

20160125UILayer

【Android 动画】动画详解之补间动画(一)

Android动画之视图动画和属性动画