動(dòng)畫(一)

詳細(xì)分析frame、bounds、center、position、anchorPoint
iOS開發(fā)系列--讓你的應(yīng)用“動(dòng)”起來
老司機(jī)帶你走進(jìn)Core Animation 之CAAnimation
iOS CoreAnimation 隱式動(dòng)畫

class ViewController: UIViewController {
    var cv: CusView!
    var bgLayer: CALayer!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let btn = UIButton(frame: CGRect(x: 50, y: 100, width: 200, height: 50 ))
        cv = CusView(frame: CGRect(x: 50  , y: btn.frame.maxY+20, width: 200, height: 200))
        cv.backgroundColor = .green
        self.view.addSubview(cv)
        bgLayer = CALayer()
        bgLayer.frame = cv.bounds
        cv.layer.addSublayer(bgLayer)
        
        
        btn.setTitle("changeColor", for: .normal)
        btn.backgroundColor = UIColor.gray
       
        view.addSubview(btn)
        btn.addTarget(self, action: #selector(clicked), for: .touchUpInside)

    }

    @objc func clicked(sender: UIButton) {

       case3()
    }
    func case1() {
        //CATransaction 只對(duì)layer生效,view無效
        CATransaction.begin()
        //        CATransaction.setDisableActions(true)
        CATransaction.setAnimationDuration(10)
        cv.layer.backgroundColor = UIColor.red.cgColor
        
        CATransaction.commit()
        CATransaction.setCompletionBlock {
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+3) {
                self.cv.layer.backgroundColor = UIColor.green.cgColor
            }
        }
    }
    func case2() {
        //CATransaction 只對(duì)layer生效,view無效
        CATransaction.begin()
        //        CATransaction.setDisableActions(true)
        CATransaction.setAnimationDuration(10)
        bgLayer.backgroundColor = UIColor.red.cgColor
        
        CATransaction.commit()
        CATransaction.setCompletionBlock {
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+3) {
                self.bgLayer.backgroundColor = UIColor.green.cgColor
            }
        }
    }
    func case3() {
        let ani = CABasicAnimation(keyPath: "backgroundColor")
        ani.duration = 3
        ani.toValue = UIColor.red.cgColor
        ani.timeOffset = 3
        ani.isRemovedOnCompletion = false
        ani.fillMode = CAMediaTimingFillMode.forwards
        cv.layer.add(ani, forKey: "colorAnimate")
      
    }
}

class CusView: UIView,CAAction {
    func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) {
        print("event:\(event)  object:\(anObject)  dict:\(dict)")

        let temp: CALayer? = anObject as? CALayer
        guard let layer = temp else { return }
        if event == "position" && layer.position == CGPoint(x: 150,y: 270) {
            let ca = CABasicAnimation(keyPath: "position")
            ca.duration = 3
            ca.toValue = CGPoint(x: self.superview?.bounds.width ?? 0/2, y: self.superview?.bounds.height ?? 0/2)
            ca.fillMode = CAMediaTimingFillMode.forwards
            ca.isRemovedOnCompletion = false
            self.layer.add(ca, forKey: "move")
        }
    }
    

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */
    override func action(for layer: CALayer, forKey event: String) -> CAAction? {
        let action = super.action(for: layer, forKey: event)
//        print("key:\(event)")
        
        return action
    }

}

case1通過CATransaction 顯示調(diào)用提交動(dòng)畫并沒有生效(把v.layer.backgroundColor換成cv.backgroundColor)和case2卻生效了,why???
通過customview override func action(for layer: CALayer, forKey event: String) -> CAAction? 斷點(diǎn),可以發(fā)現(xiàn)無論是修改view本身還是view持有的layer 都會(huì)走這個(gè)方法,并且返回了一個(gè)action為nil,so,并不會(huì)產(chǎn)生動(dòng)畫特效,UIview本身禁用了隱式動(dòng)畫的原因也是這個(gè)。UIView動(dòng)畫可以通過open class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil)類似系統(tǒng)api去實(shí)現(xiàn)。也可以在這個(gè)接口的動(dòng)畫block里面和外面調(diào)用 action(for layer:CALayer ,forKey:String)看看得到的CAAction是否為nil。
而case2 是一個(gè)自定義的layer添加到view的layer上,這個(gè)自定義layer并沒有把自己的CALayerDelegate交給任何對(duì)象,so,這個(gè)layer執(zhí)行可以動(dòng)畫的屬性時(shí)候不會(huì)走override func action(for layer: CALayer, forKey event: String) -> CAAction? 這個(gè)代理方法,而是去CALayer的Actions或者style字典搜索,然后去調(diào)用open class func defaultAction(forKey event: String) -> CAAction?,上面是一層層遞進(jìn)的如果返回結(jié)果為NSNull或者nil就會(huì)到下一步,如果最后一步仍返回nil,則無動(dòng)畫效果。
前面兩個(gè)case,雖然我們通過代碼去介入了,但具體動(dòng)畫怎么執(zhí)行的還是有系統(tǒng)完成的,這種動(dòng)畫叫隱式動(dòng)畫,而case3是通過代碼的方式添加具體動(dòng)畫行為,這種方式叫顯示動(dòng)畫

150628403626215.png

如圖中CAAnimation通過其子類添加動(dòng)畫的方式都是顯示動(dòng)畫,這個(gè)圖還差了一個(gè)CAAction協(xié)議,具體百度吧
前面提到了UIView是禁止隱式動(dòng)畫的,但是某些系統(tǒng)接口本身就帶有動(dòng)畫特效,比如UITableView 的 reloadDate或者open func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation),如果想取消動(dòng)畫效果怎么辦?可通過另外一個(gè)接口解決問題UIView.performWithoutAnimation { }
另外在CusView中遵循了CAAction代理并實(shí)現(xiàn)了其代理方法,這個(gè)是為了測(cè)試給UIView被禁用的隱式動(dòng)畫屬性添加動(dòng)畫行為,也就是實(shí)現(xiàn)CAAction去添加動(dòng)畫,再把這個(gè)CAAction實(shí)例交給CALayerDelegate 的這個(gè)接口*/
override func action(for layer: CALayer, forKey event: String) -> CAAction?具體我怎么搞,我也沒搞好,哈哈,一般不用搞這么麻煩,直接操作layer就行了,這里只是為了了解這個(gè)動(dòng)畫執(zhí)行的過程
再補(bǔ)充一點(diǎn):CATransaction這個(gè)提交動(dòng)畫的事務(wù)會(huì)在runloop中自動(dòng)創(chuàng)建,在runloop即將休眠前(kCFRunLoopBeforeWaiting)調(diào)用,是通過棧的形式管理。所以也會(huì)導(dǎo)致代碼或者說屬性已經(jīng)改變了,但動(dòng)畫還沒有執(zhí)行這種時(shí)間差。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容