Search code examples
swiftanimationuikitcagradientlayer

Make more clear CAGradientLayer animation


I created a shimmer effect animation in a tableView cell and use the following setup to show an animation:

func startShimmerAnimation() {
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)

    gradientLayer.colors = [
        UIColor.systemGray5.cgColor,
        UIColor.white.cgColor,
        UIColor.systemGray5.cgColor
    ]

    gradientLayer.locations = [0.4, 0.5, 0.6]
    gradientLayer.cornerRadius = 10
    
    let animation = CABasicAnimation(keyPath: "locations")
    animation.fromValue = [-1.0, -0.5, 0.0]
    animation.toValue = [1.0, 1.5, 2.0]
    animation.repeatCount = .infinity
    animation.duration = 1.0
    animation.repeatCount = 1
    animation.fillMode = .forwards
    animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
    
    let group = CAAnimationGroup()
    group.animations = [animation]
    group.duration = 2.0
    group.repeatCount = .infinity
    
    gradientLayer.add(group, forKey: "shimmer")
}

But take a look at the gif. There is a visible solid grey area that follows after light flash and it looks untidy.

shimmer effect

How to remove that area so after white flash there wasn't any noticeable area following it?


Solution

  • I'm not clear on exactly what effect you want to create, because your numbers are inconsistent with one another. But that inconsistency is the cause of the issue. This line:

        gradientLayer.locations = [0.4, 0.5, 0.6]
    

    suggests that you want the white part of the gradient to run from 4/10 of the width to 6/10 of the width:

    enter image description here

    But that is not what your animation says to do. Consider your fromValue:

    animation.fromValue = [-1.0, -0.5, 0.0]
    

    If we could see that gradient (which we can't, because it is off the left side of the view) it would look like this:

    enter image description here

    Let's eliminate this mismatch by making your fromValue and toValue match your locations, simply offsetting them by 1 in each direction:

        animation.fromValue = [-0.6, -0.5, -0.4]
        animation.toValue = [1.4, 1.5, 1.6]
    

    The result may not be exactly what you are after, but it doesn't have that sudden change to gray because the shimmer gradient that is moving across the screen matches the actual locations of the gradient positions:

    enter image description here

    Thinking just about that right problem area, the point here is that the pure gray occupies the entire right 4/10 of the view, whereas with your code, the pure gray doesn't occupy any of the view — the entire view, during the animation, from start to finish, is the white gradient, fading from gray at both ends.