以下のようなものを作成する。普段CALayerを利用して何かやる機会が少ないので、ちょっと久しぶりにいじってみたくなった次第。
UIKit gradetion animation · GitHub

UIViewはlayerというCALayerのプロパティを持っているが、これを派生タイプであるCAGradientLayerに置き換えたい。ただ、layerプロパティはget onlyであり、代入することができないため、今回はsublayerとしてCAGradientLayerを追加する。
import UIKit
class GradientView: UIView {
var gradientLayer: CAGradientLayer! = nil
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
override class func awakeFromNib() {
super.awakeFromNib()
}
private func setup() {
gradientLayer = CAGradientLayer()
gradientLayer.frame = CGRect(
x: 0,
y: 0,
width: layer.bounds.width,
height: layer.bounds.height
)
gradientLayer.colors = [
UIColor.orange.cgColor,
UIColor.blue.cgColor,
]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
layer.addSublayer(gradientLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = CGRect(x: 0, y: 0, width: layer.bounds.width, height: layer.bounds.height)
}
}
以下のようになる。ちなみにCAGradiationLayerのstartPointプロパティとendPointプロパティとでグラデーションの方向が決まる。

これをループするアニメーションにしていく。
とりあえず「1周期分のアニメーションを作成して、それを無限ループさせる」という考えてやっていく。
ここでの「1周期分のアニメーション」は、「左上のオレンジの部分が右下を通り過ぎ、再度左上に出てくるまで」ということである。
CALayerをアニメーションさせるにはCAAnimationを利用する。CAAnimation自体のインスタンスを生成は普通はしないため、今回はその派生タイプであるCABasicAnimationというのを利用し、アニメーションを作成する。
それぞれの色のグラデーションの終了点を表すlocationsプロパティでどこまでグラデーションをかけるのかが決まり、このプロパティをCAAnimationで徐々に変動させることでアニメーションにする。
、今回の場合はグラデーションに使う色を「橙、青、橙、青」を1列に用意しておき、その4つのlocationを短調増加させるだけで1ループ分のアニメーションとした。
わかりづらくて申し訳ないとは思う
これを実現するためにsetup関数の中身を変える。
private func setup() {
gradientLayer = CAGradientLayer()
gradientLayer.frame = CGRect(
x: 0,
y: 0,
width: layer.bounds.width,
height: layer.bounds.height
)
gradientLayer.colors = [
UIColor.orange.cgColor,
UIColor.blue.cgColor,
UIColor.orange.cgColor,
UIColor.blue.cgColor,
]
gradientLayer.locations = [
-2,
-1,
0,
1,
]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
layer.addSublayer(gradientLayer)
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [-2, -1, 0, 1]
animation.toValue = [0, 1, 2, 3]
animation.duration = 5
gradientLayer.add(animation, forKey: nil)
}
これで1ループ分のアニメーションができた。

あとはこれを無限ループさせるようにする。
CABasicAnimationにループ回数を指定するためのプロパティがあるので、これを無限回に指定すれば良い。
……setupの中身
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [-2, -1, 0, 1]
animation.toValue = [0, 1, 2, 3]
animation.duration = 5
animation.repeatCount = .infinity
gradientLayer.add(animation, forKey: nil)
}
感想
subLayerにCAGradientLayerを追加したが、そもそもUIViewのlayerClassを変更すれば良かったのではと思った。まああとの祭りやね。