詳細(xì)分析frame臊岸、bounds嘱能、center、position查蓉、anchorPoint
iOS開發(fā)系列--讓你的應(yīng)用“動”起來
老司機(jī)帶你走進(jìn)Core Animation 之CAAnimation
iOS CoreAnimation 隱式動畫
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 只對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 只對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)用提交動畫并沒有生效(把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 都會走這個方法豺憔,并且返回了一個action為nil额获,so,并不會產(chǎn)生動畫特效恭应,UIview本身禁用了隱式動畫的原因也是這個抄邀。UIView動畫可以通過open class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil)類似系統(tǒng)api去實(shí)現(xiàn)。也可以在這個接口的動畫block里面和外面調(diào)用 action(for layer:CALayer ,forKey:String)看看得到的CAAction是否為nil昼榛。
而case2 是一個自定義的layer添加到view的layer上境肾,這個自定義layer并沒有把自己的CALayerDelegate交給任何對象,so胆屿,這個layer執(zhí)行可以動畫的屬性時候不會走override func action(for layer: CALayer, forKey event: String) -> CAAction? 這個代理方法奥喻,而是去CALayer的Actions或者style字典搜索,然后去調(diào)用open class func defaultAction(forKey event: String) -> CAAction?非迹,上面是一層層遞進(jìn)的如果返回結(jié)果為NSNull或者nil就會到下一步衫嵌,如果最后一步仍返回nil,則無動畫效果彻秆。
前面兩個case,雖然我們通過代碼去介入了结闸,但具體動畫怎么執(zhí)行的還是有系統(tǒng)完成的唇兑,這種動畫叫隱式動畫,而case3是通過代碼的方式添加具體動畫行為桦锄,這種方式叫顯示動畫
如圖中CAAnimation通過其子類添加動畫的方式都是顯示動畫扎附,這個圖還差了一個CAAction協(xié)議,具體百度吧
前面提到了UIView是禁止隱式動畫的结耀,但是某些系統(tǒng)接口本身就帶有動畫特效留夜,比如UITableView 的 reloadDate或者open func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation),如果想取消動畫效果怎么辦?可通過另外一個接口解決問題UIView.performWithoutAnimation { }
另外在CusView中遵循了CAAction代理并實(shí)現(xiàn)了其代理方法图甜,這個是為了測試給UIView被禁用的隱式動畫屬性添加動畫行為碍粥,也就是實(shí)現(xiàn)CAAction去添加動畫,再把這個CAAction實(shí)例交給CALayerDelegate 的這個接口*/
override func action(for layer: CALayer, forKey event: String) -> CAAction?具體我怎么搞黑毅,我也沒搞好嚼摩,哈哈,一般不用搞這么麻煩矿瘦,直接操作layer就行了枕面,這里只是為了了解這個動畫執(zhí)行的過程
再補(bǔ)充一點(diǎn):CATransaction這個提交動畫的事務(wù)會在runloop中自動創(chuàng)建,在runloop即將休眠前(kCFRunLoopBeforeWaiting)調(diào)用缚去,是通過棧的形式管理潮秘。所以也會導(dǎo)致代碼或者說屬性已經(jīng)改變了,但動畫還沒有執(zhí)行這種時間差易结。