iOSアプリでグラデーションを表現するときは、CAGradientLayer
を使用することになると思いますが、そのグラデーションカラーをアニメーションで変化をつける方法のサンプルを記載します。
大きく分けて、CAGradientLayerの実装とアニメーションの実装、アニメーションのループの3パートにわけて記載します。
グラデーションを実装する
swiftでグラデーションを表現するには、CAGraidentLayer
インスタンスを作成し、UIView
などにinsertSublayer
します。
fileprivate let gradientStartColor: UIColor = .init(hex: "007AB7")
fileprivate let gradientEndColor: UIColor = .init(hex: "C54F91")
fileprivate lazy var gradientLayer: CAGradientLayer = {
let gradient = CAGradientLayer()
gradient.frame = view.bounds
gradient.colors = [gradientStartColor, gradientEndColor]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
gradient.drawsAsynchronously = true
return gradient
}()
view.layer.insertSublayer(gradientLayer, at: 0)
一例ですが、このようにCAGradientLayer
を作成し、UIViewController
のview
にinsertSublayer
します。
これでグラデーションを表現することが出来ます。
グラデーションのアニメーションを実装する
swiftでアニメーションを実装する一番簡単な方法は、UIView.animate
かと個人的には思いますが、こちらは出来ることが限られています。より詳細にアニメーションを実装したい場合は、CABasicAnimation
を使う必要があります。
CAGradientLayer
の何かしらのプロパティをアニメーションで変化させたいときはには、このCABasicAnimation
が必要になります。
fileprivate let animationIdentifier: String = "colorChange"
fileprivate let animationTarget: String = "colors"
let anim = CABasicAnimation(keyPath: animationTarget)
anim.fromValue = [gradientStartColor.cgColor, gradientEndColor.cgColor]
anim.toValue = [gradientEndColor.cgColor, gradientStartColor.cgColor]
anim.duration = 2.0
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
gradientLayer.add(anim, forKey: animationIdentifier)
CABasicAnimation
インスタンスをイニシャライズする際に、何をアニメーションするのかを指定します。今回は、グラデーションの色にアニメーションをつけたいので、"colors"
と指定します。
次に、変化元と変化後の値を、fromValue
とtoValue
に指定します。
isRemovedOnCompletion
は、アニメーション完了後に、アニメーション前の状態に戻すか、そのまま維持するか、を指定することが出来ます。
この状態で実行すると、1度だけアニメーションが実行されるようになります。
グラデーションのアニメーションを永続的に繰り返す
アニメーションを永続的に繰り返す最も簡単な方法は、repeatCount
に .inifinity
を指定することです。シンプルなアニメーションを実装するのであれば、これで事足りますが、今回はグラデーションカラーのアニメーションをスムーズに行いたかったので、別の方法を記載します。
// CABasicAnimationのインスタンスにdelegateを設定
anim.delegate = self
// delegateでアニメーション完了時の処理を追記
extension ViewController: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
print("Animation did stopp")
}
}
CABasicAnimation
のインスタンスの delegate
を活用して、animationの開始時・終了時に処理を記述することが出来ます。今回は、アニメーション終了時にアニメーションを再度実行する、という処理を追加することにより、アニメーションを永続的に実行するようにします。
その際に、何でも良いのですが、フラグを用いて、animationのfromValue
とtoValue
を都度入れ替え、グラデーションカラーの変化がスムーズになるように実装しています。
以下、サンプルの全コードです。
import UIKit
class ViewController: UIViewController {
fileprivate var animateFlag: Bool = true
fileprivate let animationIdentifier: String = "colorChange"
fileprivate let animationTarget: String = "colors"
fileprivate let gradientStartColor: UIColor = .init(hex: "007AB7")
fileprivate let gradientEndColor: UIColor = .init(hex: "C54F91")
fileprivate lazy var gradientLayer: CAGradientLayer = {
let gradient = CAGradientLayer()
gradient.frame = view.bounds
gradient.colors = [gradientStartColor, gradientEndColor]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
gradient.drawsAsynchronously = true
return gradient
}()
override func viewDidLoad() {
super.viewDidLoad()
view.layer.insertSublayer(gradientLayer, at: 0)
animateGradient()
}
fileprivate func animateGradient() {
let anim = CABasicAnimation(keyPath: animationTarget)
if animateFlag {
anim.fromValue = [gradientStartColor.cgColor, gradientEndColor.cgColor]
anim.toValue = [gradientEndColor.cgColor, gradientStartColor.cgColor]
} else {
anim.fromValue = [gradientEndColor.cgColor, gradientStartColor.cgColor]
anim.toValue = [gradientStartColor.cgColor, gradientEndColor.cgColor]
}
anim.duration = 2.0
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
anim.delegate = self
gradientLayer.add(anim, forKey: animationIdentifier)
}
}
extension ViewController: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
animateFlag = !animateFlag
animateGradient()
}
}
extension UIColor {
convenience init(hex: String, alpha: CGFloat = 1.0) {
let v = Int("000000" + hex, radix: 16) ?? 0
let r = CGFloat(v / Int(powf(256, 2)) % 256) / 255
let g = CGFloat(v / Int(powf(256, 1)) % 256) / 255
let b = CGFloat(v / Int(powf(256, 0)) % 256) / 255
self.init(red: r, green: g, blue: b, alpha: min(max(alpha, 0), 1))
}
}
CAGradientLayer
のアニメーションを実装する機会はこれまでなかったのですが、今回やってみて、意外と簡単に出来るものだなと思いました。
以上です。
ココナラというサービスをご存知ですか?
ココナラは、プログラミングやウェブ制作、デザインなどの専門知識を持つ人たちが、自分のスキルを活かしてサービスを提供する場所です。
初学者の方でも気軽に相談できるため、自分のスキルアップにも最適です。また、自分自身もココナラでサービスを提供することができ、収入を得ることができます。
ぜひ、ココナラに会員登録して、新しい世界を体験してみましょう!