Consider a chess game. A self-contained ViewController and a ChessPieceView appear below. (On my Xcode 10, Playgrounds do not support graphics, hence the use of a full-fledged, if minuscule, project.)
We animate the motion of the chess piece. We would also like to animate the color of a circle surrounding it (for chess tutorials and such).
Here we animate View.backgroundColor. But what we would really like to do is to animate the fill color of the circle. The backgroundColor of the piece will be the fill color of the from/to squares, whatever these are.
// ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let rect = CGRect(origin: CGPoint(x: 100, y: 100),
size: CGSize(width: 100, height: 100))
let chessPieceView = ChessPieceView.init(fromFrame: rect)
self.view.addSubview(chessPieceView)
}
}
// ChessPieceView.swift
import UIKit
class ChessPieceView: UIView {
init(fromFrame frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.cyan
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let circle = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
radius: 30,
startAngle: 0, endAngle: CGFloat.pi*2,
clockwise: false)
UIColor.red.setFill()
circle.fill()
}
override func layoutSubviews() {
super.layoutSubviews()
addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(tapPiece(_ :))))
}
@objc func tapPiece(_ gestureRecognizer : UITapGestureRecognizer ) {
guard gestureRecognizer.view != nil else { return }
if gestureRecognizer.state == .ended {
let animator = UIViewPropertyAnimator(duration: 1.2,
curve: .easeInOut,
animations: {
self.center.x += 100
self.center.y += 200
})
animator.startAnimation()
UIView.animate(withDuration: 1.2) {
self.backgroundColor = UIColor.yellow
}
}
}
}
The difficulty appears that one "can't animate the fill color of a UIBezierPath directly". That is because, it seems, iOS does not re-render (aka re-scan-convert) the path at all. When an animation is requested, a (relatively cheap) composition operation is performed instead.
