The code below creates a "shimmer" effect and works great. (The look of reflected light moving across a glossy surface.) It's currently set to repeat forever which is fine, however, I'd like there to be a pause between each shimmer. I can delay the start of the effect, but I can't seem to figure out how to delay each run. Bonus points if the delay time is random.
func startShimmering() {
let light = UIColor.white.cgColor
let alpha = UIColor.white.withAlphaComponent(0.0).cgColor
let gradient = CAGradientLayer()
gradient.colors = [alpha, light, alpha]
gradient.frame = CGRect(x: -self.bounds.size.width, y: 0, width: 3 * self.bounds.size.width, height: self.bounds.size.height)
gradient.startPoint = CGPoint(x: 1.0, y: 0.525)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
gradient.locations = [0.1, 0.5, 0.9]
self.layer.mask = gradient
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0.0, 0.1, 0.2]
animation.toValue = [0.8, 0.9, 1.0]
animation.duration = 1.5
animation.repeatCount = HUGE
gradient.add(animation, forKey: "shimmer")
}
As mentioned in Kevin's answer, you can use a CAAnimationGroup
to add a delay to the end of your shimmer animation.
The "shimmer" animation will have a duration longer than the group's animation. Any difference between these two durations will be the delay between animations.
Here is the code to do so, ready for a Swift Playground:
class ShimmerView: UIView {
func startShimmering() {
let light = UIColor.white.cgColor
let alpha = UIColor.white.withAlphaComponent(0.0).cgColor
let gradient = CAGradientLayer()
gradient.colors = [alpha, light, alpha]
gradient.frame = CGRect(x: -self.bounds.size.width, y: 0, width: 3 * self.bounds.size.width, height: self.bounds.size.height)
gradient.startPoint = CGPoint(x: 1.0, y: 0.525)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
gradient.locations = [0.1, 0.5, 0.9]
self.layer.mask = gradient
let shimmer = CABasicAnimation(keyPath: "locations")
shimmer.fromValue = [0.0, 0.1, 0.2]
shimmer.toValue = [0.8, 0.9, 1.0]
shimmer.duration = 1.5
shimmer.fillMode = kCAFillModeForwards
shimmer.isRemovedOnCompletion = false
let group = CAAnimationGroup()
group.animations = [shimmer]
group.duration = 2
group.repeatCount = HUGE
gradient.add(group, forKey: "shimmer")
}
}
let view = ShimmerView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
PlaygroundPage.current.liveView = view
view.backgroundColor = .blue
view.startShimmering()
Here is the end result:
(The gif animates the shimmer three times, which is why every third "shimmer" the delay is shorter.)
I haven't tried this myself but I believe you can wrap your animation in an animation group, set the group's duration to longer than the real animation's duration, and then set the group to repeat.
If that doesn't work, you could use a keypath animation instead, where most of the keypath is a reproduction of the normal animation, and the rest of the keypath just keeps the animation value pinned to either the start or end value.
If you want a random delay, your only choice is to actually run a timer (or use a CAAnimationDelegate
) to set up a new animation each time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With