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.