UIButton subclass with animated 'shimmer' effectAnimated magazineSubclass SKSpriteNodeAnimated Score Amounts for GameSliding effect with bounceThree-layer parallax effectCustom animated image controlAnimated Ticker with JavaScript and Sass CSSCSS3 Animated LoaderRain effect with SFMLTableview Subclass implementing delegate datasource

Why do Radio Buttons not fill the entire outer circle?

Stack Interview Code methods made from class Node and Smart Pointers

Non-trope happy ending?

How to draw a matrix with arrows in limited space

Is this toilet slogan correct usage of the English language?

Does grappling negate Mirror Image?

The IT department bottlenecks progress, how should I handle this?

Quoting Keynes in a lecture

Is it allowed to activate the ability of multiple planeswalkers in a single turn?

C++ copy constructor called at return

Why is it that I can sometimes guess the next note?

Doesn't the system of the Supreme Court oppose justice?

Can you use Vicious Mockery to win an argument or gain favours?

awk assign to multiple variables at once

Review your own paper in Mathematics

Is there a nicer/politer/more positive alternative for "negates"?

How could a planet have erratic days?

Why should universal income be universal?

Mimic lecturing on blackboard, facing audience

Permission on Database

Will number of steps recorded on FitBit/any fitness tracker add up distance in PokemonGo?

Has any country ever had 2 former presidents in jail simultaneously?

Did the UK lift the requirement for registering SIM cards?

Biological Blimps: Propulsion



UIButton subclass with animated 'shimmer' effect


Animated magazineSubclass SKSpriteNodeAnimated Score Amounts for GameSliding effect with bounceThree-layer parallax effectCustom animated image controlAnimated Ticker with JavaScript and Sass CSSCSS3 Animated LoaderRain effect with SFMLTableview Subclass implementing delegate datasource













6












$begingroup$


In earlier versions of iOS the lock screen had a 'slide to unlock' element which I'm referencing as a 'shimmer' effect.



The effect I'm looking for is simpler:



  1. Button starts with single color (e.g. blue)

  2. a band of color (e.g. red) sweeps across the text label from left to right

  3. Repeat

Here's an example of my ShimmerButton class in action, and the code itself:



enter image description here



class ShimmerButton: UIButton 

private let wrapperLayer = CALayer()
private let gradientLayer = CAGradientLayer()

var gradientColors: [UIColor] = []
didSet
gradientLayer.colors = gradientColors.map( $0.cgColor )



override func layoutSubviews()
super.layoutSubviews()

// only needs to be set once, but no harm (?) in setting multiple times
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
wrapperLayer.addSublayer(gradientLayer)
layer.insertSublayer(wrapperLayer, at: 0)
wrapperLayer.mask = titleLabel?.layer

// update sublayers based on new frame
wrapperLayer.frame = frame
gradientLayer.frame.size = CGSize(width: frame.width * 4, height: frame.height)

// remove any existing animation, and re-create for new size
let animationKeyPath = "position.x"

gradientLayer.removeAnimation(forKey: animationKeyPath)

let animation: CABasicAnimation = CABasicAnimation(keyPath: animationKeyPath)

animation.fromValue = bounds.width - gradientLayer.bounds.width / 2
animation.toValue = gradientLayer.bounds.width / 2
animation.duration = 3

animation.repeatCount = HUGE
animation.fillMode = kCAFillModeForwards
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

gradientLayer.add(animation, forKey: animationKeyPath)




Example usage:



let shimmer = ShimmerButton(frame: .zero)
shimmer.backgroundColor = .white
shimmer.setTitle("Find new skills...", for: .normal)
shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
shimmer.sizeToFit()
shimmer.gradientColors = [.blue, .blue, .red, .blue, .blue]


Some questions:



  1. Code that only needs to be once off is happening in layoutSubviews so will be called multiple times. I did this so I didn't have to override init?(coder:) and init(frame:). Is this acceptable, or just lazy on my part?

  2. I'm animating the position of a CAGradientLayer to get the visual effect, but found that I needed to use another layer as a wrapper otherwise the text itself would move. Am I overlooking a solution that involves fewer layers?









share|improve this question











$endgroup$
















    6












    $begingroup$


    In earlier versions of iOS the lock screen had a 'slide to unlock' element which I'm referencing as a 'shimmer' effect.



    The effect I'm looking for is simpler:



    1. Button starts with single color (e.g. blue)

    2. a band of color (e.g. red) sweeps across the text label from left to right

    3. Repeat

    Here's an example of my ShimmerButton class in action, and the code itself:



    enter image description here



    class ShimmerButton: UIButton 

    private let wrapperLayer = CALayer()
    private let gradientLayer = CAGradientLayer()

    var gradientColors: [UIColor] = []
    didSet
    gradientLayer.colors = gradientColors.map( $0.cgColor )



    override func layoutSubviews()
    super.layoutSubviews()

    // only needs to be set once, but no harm (?) in setting multiple times
    gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
    wrapperLayer.addSublayer(gradientLayer)
    layer.insertSublayer(wrapperLayer, at: 0)
    wrapperLayer.mask = titleLabel?.layer

    // update sublayers based on new frame
    wrapperLayer.frame = frame
    gradientLayer.frame.size = CGSize(width: frame.width * 4, height: frame.height)

    // remove any existing animation, and re-create for new size
    let animationKeyPath = "position.x"

    gradientLayer.removeAnimation(forKey: animationKeyPath)

    let animation: CABasicAnimation = CABasicAnimation(keyPath: animationKeyPath)

    animation.fromValue = bounds.width - gradientLayer.bounds.width / 2
    animation.toValue = gradientLayer.bounds.width / 2
    animation.duration = 3

    animation.repeatCount = HUGE
    animation.fillMode = kCAFillModeForwards
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

    gradientLayer.add(animation, forKey: animationKeyPath)




    Example usage:



    let shimmer = ShimmerButton(frame: .zero)
    shimmer.backgroundColor = .white
    shimmer.setTitle("Find new skills...", for: .normal)
    shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
    shimmer.sizeToFit()
    shimmer.gradientColors = [.blue, .blue, .red, .blue, .blue]


    Some questions:



    1. Code that only needs to be once off is happening in layoutSubviews so will be called multiple times. I did this so I didn't have to override init?(coder:) and init(frame:). Is this acceptable, or just lazy on my part?

    2. I'm animating the position of a CAGradientLayer to get the visual effect, but found that I needed to use another layer as a wrapper otherwise the text itself would move. Am I overlooking a solution that involves fewer layers?









    share|improve this question











    $endgroup$














      6












      6








      6


      4



      $begingroup$


      In earlier versions of iOS the lock screen had a 'slide to unlock' element which I'm referencing as a 'shimmer' effect.



      The effect I'm looking for is simpler:



      1. Button starts with single color (e.g. blue)

      2. a band of color (e.g. red) sweeps across the text label from left to right

      3. Repeat

      Here's an example of my ShimmerButton class in action, and the code itself:



      enter image description here



      class ShimmerButton: UIButton 

      private let wrapperLayer = CALayer()
      private let gradientLayer = CAGradientLayer()

      var gradientColors: [UIColor] = []
      didSet
      gradientLayer.colors = gradientColors.map( $0.cgColor )



      override func layoutSubviews()
      super.layoutSubviews()

      // only needs to be set once, but no harm (?) in setting multiple times
      gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
      gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
      wrapperLayer.addSublayer(gradientLayer)
      layer.insertSublayer(wrapperLayer, at: 0)
      wrapperLayer.mask = titleLabel?.layer

      // update sublayers based on new frame
      wrapperLayer.frame = frame
      gradientLayer.frame.size = CGSize(width: frame.width * 4, height: frame.height)

      // remove any existing animation, and re-create for new size
      let animationKeyPath = "position.x"

      gradientLayer.removeAnimation(forKey: animationKeyPath)

      let animation: CABasicAnimation = CABasicAnimation(keyPath: animationKeyPath)

      animation.fromValue = bounds.width - gradientLayer.bounds.width / 2
      animation.toValue = gradientLayer.bounds.width / 2
      animation.duration = 3

      animation.repeatCount = HUGE
      animation.fillMode = kCAFillModeForwards
      animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

      gradientLayer.add(animation, forKey: animationKeyPath)




      Example usage:



      let shimmer = ShimmerButton(frame: .zero)
      shimmer.backgroundColor = .white
      shimmer.setTitle("Find new skills...", for: .normal)
      shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
      shimmer.sizeToFit()
      shimmer.gradientColors = [.blue, .blue, .red, .blue, .blue]


      Some questions:



      1. Code that only needs to be once off is happening in layoutSubviews so will be called multiple times. I did this so I didn't have to override init?(coder:) and init(frame:). Is this acceptable, or just lazy on my part?

      2. I'm animating the position of a CAGradientLayer to get the visual effect, but found that I needed to use another layer as a wrapper otherwise the text itself would move. Am I overlooking a solution that involves fewer layers?









      share|improve this question











      $endgroup$




      In earlier versions of iOS the lock screen had a 'slide to unlock' element which I'm referencing as a 'shimmer' effect.



      The effect I'm looking for is simpler:



      1. Button starts with single color (e.g. blue)

      2. a band of color (e.g. red) sweeps across the text label from left to right

      3. Repeat

      Here's an example of my ShimmerButton class in action, and the code itself:



      enter image description here



      class ShimmerButton: UIButton 

      private let wrapperLayer = CALayer()
      private let gradientLayer = CAGradientLayer()

      var gradientColors: [UIColor] = []
      didSet
      gradientLayer.colors = gradientColors.map( $0.cgColor )



      override func layoutSubviews()
      super.layoutSubviews()

      // only needs to be set once, but no harm (?) in setting multiple times
      gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
      gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
      wrapperLayer.addSublayer(gradientLayer)
      layer.insertSublayer(wrapperLayer, at: 0)
      wrapperLayer.mask = titleLabel?.layer

      // update sublayers based on new frame
      wrapperLayer.frame = frame
      gradientLayer.frame.size = CGSize(width: frame.width * 4, height: frame.height)

      // remove any existing animation, and re-create for new size
      let animationKeyPath = "position.x"

      gradientLayer.removeAnimation(forKey: animationKeyPath)

      let animation: CABasicAnimation = CABasicAnimation(keyPath: animationKeyPath)

      animation.fromValue = bounds.width - gradientLayer.bounds.width / 2
      animation.toValue = gradientLayer.bounds.width / 2
      animation.duration = 3

      animation.repeatCount = HUGE
      animation.fillMode = kCAFillModeForwards
      animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

      gradientLayer.add(animation, forKey: animationKeyPath)




      Example usage:



      let shimmer = ShimmerButton(frame: .zero)
      shimmer.backgroundColor = .white
      shimmer.setTitle("Find new skills...", for: .normal)
      shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
      shimmer.sizeToFit()
      shimmer.gradientColors = [.blue, .blue, .red, .blue, .blue]


      Some questions:



      1. Code that only needs to be once off is happening in layoutSubviews so will be called multiple times. I did this so I didn't have to override init?(coder:) and init(frame:). Is this acceptable, or just lazy on my part?

      2. I'm animating the position of a CAGradientLayer to get the visual effect, but found that I needed to use another layer as a wrapper otherwise the text itself would move. Am I overlooking a solution that involves fewer layers?






      swift animation swift3






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Mar 26 '17 at 16:30









      Jamal

      30.4k11121227




      30.4k11121227










      asked Mar 20 '17 at 20:07









      MathewSMathewS

      568311




      568311




















          2 Answers
          2






          active

          oldest

          votes


















          6












          $begingroup$

          Okay, so sometimes posting a question is the best way to figure out an answer yourself 🙃.



          I was looking for an alternative to having the gradient colors evenly spaced (my hack was to repeat the colors e.g. "blue blue red blue blue") and found the locations property on CAGradientLayer which is also animatable.



          Animating this property feels like a much better approach because with the position not changing I can remove the wrapper layer.



          Then, with without the wrapper layer I realized that I could override layerClass so the buttons backing layer is a gradient layer, which will also resize as needed when the view frame changes so that I don't even need to override layoutSubviews.



          The only thing that feels a little strange is the forced requirement for for gradient colors to have three colors (otherwise I'd need to figure some formula to derive values for locations).



          Edit: I've updated answer so instead of directly setting the colors, I've exposed gradientTint and gradientHighlight properties that are used to set the gradients colors array.



          I've created a protocol that captures the properties used to define the shimmer effect, and also provide a default implementation of the animation.



          It wasn't a requirement from my original question, but moving this code out of a specific subclass makes this snippet of code reusable (and maintainable) across other subclasses (e.g. UIView, UILabel).



          protocol ShimmerEffect 
          var animationDuration: TimeInterval set get
          var animationDelay: TimeInterval set get

          var gradientTint: UIColor set get
          var gradientHighlight: UIColor set get

          //// Expects value between 0.0—1.0 that represents
          //// the ratio of the gradient highlight to the full
          //// width of the gradient.
          var gradientHighlightRatio: Double set get

          //// The layer that the gradient will be applied to
          var gradientLayer: CAGradientLayer get



          Default implementation:



          extension ShimmerEffect 

          /// Configures, and adds the animation to the gradientLayer
          func addShimmerAnimation()

          // `gradientHighlightRatio` represents how wide the highlight
          // should be compared to the entire width of the gradient and
          // is used to calculate the positions of the 3 gradient colors.
          // If the highlight is 20% width of the gradient, then the
          // 'start locations' would be [-0.2, -0.1, 0.0] and the
          // 'end locations' would be [1.0, 1.1, 1.2]
          let startLocations = [NSNumber(value: -gradientHighlightRatio), NSNumber(value: -gradientHighlightRatio/2), 0.0]
          let endLocations = [1, NSNumber(value: 1+(gradientHighlightRatio/2)), NSNumber(value: 1+gradientHighlightRatio)]
          let gradientColors = [gradientTint.cgColor, gradientHighlight.cgColor, gradientTint.cgColor]

          // If the gradient highlight ratio is wide, then it can
          // 'bleed' over into the visible space of the view, which
          // looks particularly bad if there is a pause between the
          // animation repeating.
          // Shifting the start and end points of the gradient by the
          // size of the highlight prevents this.
          gradientLayer.startPoint = CGPoint(x: -gradientHighlightRatio, y: 0.5)
          gradientLayer.endPoint = CGPoint(x: 1+gradientHighlightRatio, y: 0.5)
          gradientLayer.locations = startLocations
          gradientLayer.colors = gradientColors

          let animationKeyPath = "locations"

          let shimmerAnimation = CABasicAnimation(keyPath: animationKeyPath)
          shimmerAnimation.fromValue = startLocations
          shimmerAnimation.toValue = endLocations
          shimmerAnimation.duration = animationDuration
          shimmerAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

          let animationGroup = CAAnimationGroup()
          animationGroup.duration = animationDuration + animationDelay
          animationGroup.repeatCount = .infinity
          animationGroup.animations = [shimmerAnimation]

          // removes animation with same key (if exists) then adds
          // the new animation
          gradientLayer.removeAnimation(forKey: animationKeyPath)
          gradientLayer.add(animationGroup, forKey: animationKeyPath)




          In the UIButton subclass I've added property observers to each of the properties that calls addShimmerAnimation() with any property change.



          I also considered just supplying default values and requiring addShimmerAnimation() to be called manually once properties were configured. Another route was not having any public properties exposed and instead passing everything in through an initializer, but that would remove the possibility of these classes being used in a storyboard (which is an option I like to leave open) and having properties exposed through tagging the properties with IBInspectable.



          class ShimmerButton: UIButton, ShimmerEffect 

          override static var layerClass: AnyClass
          return CAGradientLayer.self


          var gradientLayer: CAGradientLayer
          return layer as! CAGradientLayer


          var animationDuration: TimeInterval = 3
          didSet addShimmerAnimation()

          var animationDelay: TimeInterval = 1.5
          didSet addShimmerAnimation()


          var gradientHighlightRatio: Double = 0.3
          didSet addShimmerAnimation()


          var gradientTint: UIColor = .black
          didSet addShimmerAnimation()


          var gradientHighlight: UIColor = .white
          didSet addShimmerAnimation()


          override init(frame: CGRect)
          super.init(frame: frame)
          gradientLayer.mask = titleLabel?.layer
          addShimmerAnimation()


          required init?(coder aDecoder: NSCoder)
          super.init(coder: aDecoder)
          gradientLayer.mask = titleLabel?.layer
          addShimmerAnimation()




          Example usage:



          let shimmer = ShimmerButton()
          shimmer.setTitle("Find new skills", for: .normal)
          shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
          shimmer.gradientTint = darkBlue
          shimmer.gradientHighlight = lightBlue
          shimmer.sizeToFit()


          What I like about this approach is that the complexity is moved out of the subclass making it super easy to duplicate over other views.



          What frustrates me is that UIView, UILabel and UIButton only have minor differences. I wish there was a way for the computed properties to be extracted into a common place.



          Example of ShimmerButton and ShimmerView (a UIView subclass) being used together:



          enter image description here






          share|improve this answer











          $endgroup$




















            0












            $begingroup$

            Shimmer animation can be added like below in iOS



            class ViewController: UIViewController 
            @IBOutlet var label: UILabel!
            let gradientLayer = CAGradientLayer()
            override func viewDidLoad()
            super.viewDidLoad()
            self.gradientLayer.frame = self.label.bounds
            self.gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
            self.gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
            self.gradientLayer.colors = [UIColor.red.cgColor, UIColor.white.cgColor, UIColor.darkGray.cgColor]
            let startLocations : [NSNumber] = [-1.0,-0.5, 0.0]
            let endLocations : [NSNumber] = [1.0,1.5, 2.0]

            self.gradientLayer.locations = startLocations
            let animation = CABasicAnimation(keyPath: "locations")
            animation.fromValue = startLocations
            animation.toValue = endLocations
            animation.duration = 0.8
            animation.repeatCount = .infinity

            self.gradientLayer.add(animation, forKey: animation.keyPath)
            self.label.layer.addSublayer(self.gradientLayer)

            DispatchQueue.main.asyncAfter(deadline: .now() + 5)
            self.gradientLayer.removeAllAnimations()









            share








            New contributor




            Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$












              Your Answer





              StackExchange.ifUsing("editor", function ()
              return StackExchange.using("mathjaxEditing", function ()
              StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
              StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
              );
              );
              , "mathjax-editing");

              StackExchange.ifUsing("editor", function ()
              StackExchange.using("externalEditor", function ()
              StackExchange.using("snippets", function ()
              StackExchange.snippets.init();
              );
              );
              , "code-snippets");

              StackExchange.ready(function()
              var channelOptions =
              tags: "".split(" "),
              id: "196"
              ;
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function()
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled)
              StackExchange.using("snippets", function()
              createEditor();
              );

              else
              createEditor();

              );

              function createEditor()
              StackExchange.prepareEditor(
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: false,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              imageUploader:
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              ,
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              );



              );













              draft saved

              draft discarded


















              StackExchange.ready(
              function ()
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f158336%2fuibutton-subclass-with-animated-shimmer-effect%23new-answer', 'question_page');

              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              6












              $begingroup$

              Okay, so sometimes posting a question is the best way to figure out an answer yourself 🙃.



              I was looking for an alternative to having the gradient colors evenly spaced (my hack was to repeat the colors e.g. "blue blue red blue blue") and found the locations property on CAGradientLayer which is also animatable.



              Animating this property feels like a much better approach because with the position not changing I can remove the wrapper layer.



              Then, with without the wrapper layer I realized that I could override layerClass so the buttons backing layer is a gradient layer, which will also resize as needed when the view frame changes so that I don't even need to override layoutSubviews.



              The only thing that feels a little strange is the forced requirement for for gradient colors to have three colors (otherwise I'd need to figure some formula to derive values for locations).



              Edit: I've updated answer so instead of directly setting the colors, I've exposed gradientTint and gradientHighlight properties that are used to set the gradients colors array.



              I've created a protocol that captures the properties used to define the shimmer effect, and also provide a default implementation of the animation.



              It wasn't a requirement from my original question, but moving this code out of a specific subclass makes this snippet of code reusable (and maintainable) across other subclasses (e.g. UIView, UILabel).



              protocol ShimmerEffect 
              var animationDuration: TimeInterval set get
              var animationDelay: TimeInterval set get

              var gradientTint: UIColor set get
              var gradientHighlight: UIColor set get

              //// Expects value between 0.0—1.0 that represents
              //// the ratio of the gradient highlight to the full
              //// width of the gradient.
              var gradientHighlightRatio: Double set get

              //// The layer that the gradient will be applied to
              var gradientLayer: CAGradientLayer get



              Default implementation:



              extension ShimmerEffect 

              /// Configures, and adds the animation to the gradientLayer
              func addShimmerAnimation()

              // `gradientHighlightRatio` represents how wide the highlight
              // should be compared to the entire width of the gradient and
              // is used to calculate the positions of the 3 gradient colors.
              // If the highlight is 20% width of the gradient, then the
              // 'start locations' would be [-0.2, -0.1, 0.0] and the
              // 'end locations' would be [1.0, 1.1, 1.2]
              let startLocations = [NSNumber(value: -gradientHighlightRatio), NSNumber(value: -gradientHighlightRatio/2), 0.0]
              let endLocations = [1, NSNumber(value: 1+(gradientHighlightRatio/2)), NSNumber(value: 1+gradientHighlightRatio)]
              let gradientColors = [gradientTint.cgColor, gradientHighlight.cgColor, gradientTint.cgColor]

              // If the gradient highlight ratio is wide, then it can
              // 'bleed' over into the visible space of the view, which
              // looks particularly bad if there is a pause between the
              // animation repeating.
              // Shifting the start and end points of the gradient by the
              // size of the highlight prevents this.
              gradientLayer.startPoint = CGPoint(x: -gradientHighlightRatio, y: 0.5)
              gradientLayer.endPoint = CGPoint(x: 1+gradientHighlightRatio, y: 0.5)
              gradientLayer.locations = startLocations
              gradientLayer.colors = gradientColors

              let animationKeyPath = "locations"

              let shimmerAnimation = CABasicAnimation(keyPath: animationKeyPath)
              shimmerAnimation.fromValue = startLocations
              shimmerAnimation.toValue = endLocations
              shimmerAnimation.duration = animationDuration
              shimmerAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

              let animationGroup = CAAnimationGroup()
              animationGroup.duration = animationDuration + animationDelay
              animationGroup.repeatCount = .infinity
              animationGroup.animations = [shimmerAnimation]

              // removes animation with same key (if exists) then adds
              // the new animation
              gradientLayer.removeAnimation(forKey: animationKeyPath)
              gradientLayer.add(animationGroup, forKey: animationKeyPath)




              In the UIButton subclass I've added property observers to each of the properties that calls addShimmerAnimation() with any property change.



              I also considered just supplying default values and requiring addShimmerAnimation() to be called manually once properties were configured. Another route was not having any public properties exposed and instead passing everything in through an initializer, but that would remove the possibility of these classes being used in a storyboard (which is an option I like to leave open) and having properties exposed through tagging the properties with IBInspectable.



              class ShimmerButton: UIButton, ShimmerEffect 

              override static var layerClass: AnyClass
              return CAGradientLayer.self


              var gradientLayer: CAGradientLayer
              return layer as! CAGradientLayer


              var animationDuration: TimeInterval = 3
              didSet addShimmerAnimation()

              var animationDelay: TimeInterval = 1.5
              didSet addShimmerAnimation()


              var gradientHighlightRatio: Double = 0.3
              didSet addShimmerAnimation()


              var gradientTint: UIColor = .black
              didSet addShimmerAnimation()


              var gradientHighlight: UIColor = .white
              didSet addShimmerAnimation()


              override init(frame: CGRect)
              super.init(frame: frame)
              gradientLayer.mask = titleLabel?.layer
              addShimmerAnimation()


              required init?(coder aDecoder: NSCoder)
              super.init(coder: aDecoder)
              gradientLayer.mask = titleLabel?.layer
              addShimmerAnimation()




              Example usage:



              let shimmer = ShimmerButton()
              shimmer.setTitle("Find new skills", for: .normal)
              shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
              shimmer.gradientTint = darkBlue
              shimmer.gradientHighlight = lightBlue
              shimmer.sizeToFit()


              What I like about this approach is that the complexity is moved out of the subclass making it super easy to duplicate over other views.



              What frustrates me is that UIView, UILabel and UIButton only have minor differences. I wish there was a way for the computed properties to be extracted into a common place.



              Example of ShimmerButton and ShimmerView (a UIView subclass) being used together:



              enter image description here






              share|improve this answer











              $endgroup$

















                6












                $begingroup$

                Okay, so sometimes posting a question is the best way to figure out an answer yourself 🙃.



                I was looking for an alternative to having the gradient colors evenly spaced (my hack was to repeat the colors e.g. "blue blue red blue blue") and found the locations property on CAGradientLayer which is also animatable.



                Animating this property feels like a much better approach because with the position not changing I can remove the wrapper layer.



                Then, with without the wrapper layer I realized that I could override layerClass so the buttons backing layer is a gradient layer, which will also resize as needed when the view frame changes so that I don't even need to override layoutSubviews.



                The only thing that feels a little strange is the forced requirement for for gradient colors to have three colors (otherwise I'd need to figure some formula to derive values for locations).



                Edit: I've updated answer so instead of directly setting the colors, I've exposed gradientTint and gradientHighlight properties that are used to set the gradients colors array.



                I've created a protocol that captures the properties used to define the shimmer effect, and also provide a default implementation of the animation.



                It wasn't a requirement from my original question, but moving this code out of a specific subclass makes this snippet of code reusable (and maintainable) across other subclasses (e.g. UIView, UILabel).



                protocol ShimmerEffect 
                var animationDuration: TimeInterval set get
                var animationDelay: TimeInterval set get

                var gradientTint: UIColor set get
                var gradientHighlight: UIColor set get

                //// Expects value between 0.0—1.0 that represents
                //// the ratio of the gradient highlight to the full
                //// width of the gradient.
                var gradientHighlightRatio: Double set get

                //// The layer that the gradient will be applied to
                var gradientLayer: CAGradientLayer get



                Default implementation:



                extension ShimmerEffect 

                /// Configures, and adds the animation to the gradientLayer
                func addShimmerAnimation()

                // `gradientHighlightRatio` represents how wide the highlight
                // should be compared to the entire width of the gradient and
                // is used to calculate the positions of the 3 gradient colors.
                // If the highlight is 20% width of the gradient, then the
                // 'start locations' would be [-0.2, -0.1, 0.0] and the
                // 'end locations' would be [1.0, 1.1, 1.2]
                let startLocations = [NSNumber(value: -gradientHighlightRatio), NSNumber(value: -gradientHighlightRatio/2), 0.0]
                let endLocations = [1, NSNumber(value: 1+(gradientHighlightRatio/2)), NSNumber(value: 1+gradientHighlightRatio)]
                let gradientColors = [gradientTint.cgColor, gradientHighlight.cgColor, gradientTint.cgColor]

                // If the gradient highlight ratio is wide, then it can
                // 'bleed' over into the visible space of the view, which
                // looks particularly bad if there is a pause between the
                // animation repeating.
                // Shifting the start and end points of the gradient by the
                // size of the highlight prevents this.
                gradientLayer.startPoint = CGPoint(x: -gradientHighlightRatio, y: 0.5)
                gradientLayer.endPoint = CGPoint(x: 1+gradientHighlightRatio, y: 0.5)
                gradientLayer.locations = startLocations
                gradientLayer.colors = gradientColors

                let animationKeyPath = "locations"

                let shimmerAnimation = CABasicAnimation(keyPath: animationKeyPath)
                shimmerAnimation.fromValue = startLocations
                shimmerAnimation.toValue = endLocations
                shimmerAnimation.duration = animationDuration
                shimmerAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

                let animationGroup = CAAnimationGroup()
                animationGroup.duration = animationDuration + animationDelay
                animationGroup.repeatCount = .infinity
                animationGroup.animations = [shimmerAnimation]

                // removes animation with same key (if exists) then adds
                // the new animation
                gradientLayer.removeAnimation(forKey: animationKeyPath)
                gradientLayer.add(animationGroup, forKey: animationKeyPath)




                In the UIButton subclass I've added property observers to each of the properties that calls addShimmerAnimation() with any property change.



                I also considered just supplying default values and requiring addShimmerAnimation() to be called manually once properties were configured. Another route was not having any public properties exposed and instead passing everything in through an initializer, but that would remove the possibility of these classes being used in a storyboard (which is an option I like to leave open) and having properties exposed through tagging the properties with IBInspectable.



                class ShimmerButton: UIButton, ShimmerEffect 

                override static var layerClass: AnyClass
                return CAGradientLayer.self


                var gradientLayer: CAGradientLayer
                return layer as! CAGradientLayer


                var animationDuration: TimeInterval = 3
                didSet addShimmerAnimation()

                var animationDelay: TimeInterval = 1.5
                didSet addShimmerAnimation()


                var gradientHighlightRatio: Double = 0.3
                didSet addShimmerAnimation()


                var gradientTint: UIColor = .black
                didSet addShimmerAnimation()


                var gradientHighlight: UIColor = .white
                didSet addShimmerAnimation()


                override init(frame: CGRect)
                super.init(frame: frame)
                gradientLayer.mask = titleLabel?.layer
                addShimmerAnimation()


                required init?(coder aDecoder: NSCoder)
                super.init(coder: aDecoder)
                gradientLayer.mask = titleLabel?.layer
                addShimmerAnimation()




                Example usage:



                let shimmer = ShimmerButton()
                shimmer.setTitle("Find new skills", for: .normal)
                shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
                shimmer.gradientTint = darkBlue
                shimmer.gradientHighlight = lightBlue
                shimmer.sizeToFit()


                What I like about this approach is that the complexity is moved out of the subclass making it super easy to duplicate over other views.



                What frustrates me is that UIView, UILabel and UIButton only have minor differences. I wish there was a way for the computed properties to be extracted into a common place.



                Example of ShimmerButton and ShimmerView (a UIView subclass) being used together:



                enter image description here






                share|improve this answer











                $endgroup$















                  6












                  6








                  6





                  $begingroup$

                  Okay, so sometimes posting a question is the best way to figure out an answer yourself 🙃.



                  I was looking for an alternative to having the gradient colors evenly spaced (my hack was to repeat the colors e.g. "blue blue red blue blue") and found the locations property on CAGradientLayer which is also animatable.



                  Animating this property feels like a much better approach because with the position not changing I can remove the wrapper layer.



                  Then, with without the wrapper layer I realized that I could override layerClass so the buttons backing layer is a gradient layer, which will also resize as needed when the view frame changes so that I don't even need to override layoutSubviews.



                  The only thing that feels a little strange is the forced requirement for for gradient colors to have three colors (otherwise I'd need to figure some formula to derive values for locations).



                  Edit: I've updated answer so instead of directly setting the colors, I've exposed gradientTint and gradientHighlight properties that are used to set the gradients colors array.



                  I've created a protocol that captures the properties used to define the shimmer effect, and also provide a default implementation of the animation.



                  It wasn't a requirement from my original question, but moving this code out of a specific subclass makes this snippet of code reusable (and maintainable) across other subclasses (e.g. UIView, UILabel).



                  protocol ShimmerEffect 
                  var animationDuration: TimeInterval set get
                  var animationDelay: TimeInterval set get

                  var gradientTint: UIColor set get
                  var gradientHighlight: UIColor set get

                  //// Expects value between 0.0—1.0 that represents
                  //// the ratio of the gradient highlight to the full
                  //// width of the gradient.
                  var gradientHighlightRatio: Double set get

                  //// The layer that the gradient will be applied to
                  var gradientLayer: CAGradientLayer get



                  Default implementation:



                  extension ShimmerEffect 

                  /// Configures, and adds the animation to the gradientLayer
                  func addShimmerAnimation()

                  // `gradientHighlightRatio` represents how wide the highlight
                  // should be compared to the entire width of the gradient and
                  // is used to calculate the positions of the 3 gradient colors.
                  // If the highlight is 20% width of the gradient, then the
                  // 'start locations' would be [-0.2, -0.1, 0.0] and the
                  // 'end locations' would be [1.0, 1.1, 1.2]
                  let startLocations = [NSNumber(value: -gradientHighlightRatio), NSNumber(value: -gradientHighlightRatio/2), 0.0]
                  let endLocations = [1, NSNumber(value: 1+(gradientHighlightRatio/2)), NSNumber(value: 1+gradientHighlightRatio)]
                  let gradientColors = [gradientTint.cgColor, gradientHighlight.cgColor, gradientTint.cgColor]

                  // If the gradient highlight ratio is wide, then it can
                  // 'bleed' over into the visible space of the view, which
                  // looks particularly bad if there is a pause between the
                  // animation repeating.
                  // Shifting the start and end points of the gradient by the
                  // size of the highlight prevents this.
                  gradientLayer.startPoint = CGPoint(x: -gradientHighlightRatio, y: 0.5)
                  gradientLayer.endPoint = CGPoint(x: 1+gradientHighlightRatio, y: 0.5)
                  gradientLayer.locations = startLocations
                  gradientLayer.colors = gradientColors

                  let animationKeyPath = "locations"

                  let shimmerAnimation = CABasicAnimation(keyPath: animationKeyPath)
                  shimmerAnimation.fromValue = startLocations
                  shimmerAnimation.toValue = endLocations
                  shimmerAnimation.duration = animationDuration
                  shimmerAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

                  let animationGroup = CAAnimationGroup()
                  animationGroup.duration = animationDuration + animationDelay
                  animationGroup.repeatCount = .infinity
                  animationGroup.animations = [shimmerAnimation]

                  // removes animation with same key (if exists) then adds
                  // the new animation
                  gradientLayer.removeAnimation(forKey: animationKeyPath)
                  gradientLayer.add(animationGroup, forKey: animationKeyPath)




                  In the UIButton subclass I've added property observers to each of the properties that calls addShimmerAnimation() with any property change.



                  I also considered just supplying default values and requiring addShimmerAnimation() to be called manually once properties were configured. Another route was not having any public properties exposed and instead passing everything in through an initializer, but that would remove the possibility of these classes being used in a storyboard (which is an option I like to leave open) and having properties exposed through tagging the properties with IBInspectable.



                  class ShimmerButton: UIButton, ShimmerEffect 

                  override static var layerClass: AnyClass
                  return CAGradientLayer.self


                  var gradientLayer: CAGradientLayer
                  return layer as! CAGradientLayer


                  var animationDuration: TimeInterval = 3
                  didSet addShimmerAnimation()

                  var animationDelay: TimeInterval = 1.5
                  didSet addShimmerAnimation()


                  var gradientHighlightRatio: Double = 0.3
                  didSet addShimmerAnimation()


                  var gradientTint: UIColor = .black
                  didSet addShimmerAnimation()


                  var gradientHighlight: UIColor = .white
                  didSet addShimmerAnimation()


                  override init(frame: CGRect)
                  super.init(frame: frame)
                  gradientLayer.mask = titleLabel?.layer
                  addShimmerAnimation()


                  required init?(coder aDecoder: NSCoder)
                  super.init(coder: aDecoder)
                  gradientLayer.mask = titleLabel?.layer
                  addShimmerAnimation()




                  Example usage:



                  let shimmer = ShimmerButton()
                  shimmer.setTitle("Find new skills", for: .normal)
                  shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
                  shimmer.gradientTint = darkBlue
                  shimmer.gradientHighlight = lightBlue
                  shimmer.sizeToFit()


                  What I like about this approach is that the complexity is moved out of the subclass making it super easy to duplicate over other views.



                  What frustrates me is that UIView, UILabel and UIButton only have minor differences. I wish there was a way for the computed properties to be extracted into a common place.



                  Example of ShimmerButton and ShimmerView (a UIView subclass) being used together:



                  enter image description here






                  share|improve this answer











                  $endgroup$



                  Okay, so sometimes posting a question is the best way to figure out an answer yourself 🙃.



                  I was looking for an alternative to having the gradient colors evenly spaced (my hack was to repeat the colors e.g. "blue blue red blue blue") and found the locations property on CAGradientLayer which is also animatable.



                  Animating this property feels like a much better approach because with the position not changing I can remove the wrapper layer.



                  Then, with without the wrapper layer I realized that I could override layerClass so the buttons backing layer is a gradient layer, which will also resize as needed when the view frame changes so that I don't even need to override layoutSubviews.



                  The only thing that feels a little strange is the forced requirement for for gradient colors to have three colors (otherwise I'd need to figure some formula to derive values for locations).



                  Edit: I've updated answer so instead of directly setting the colors, I've exposed gradientTint and gradientHighlight properties that are used to set the gradients colors array.



                  I've created a protocol that captures the properties used to define the shimmer effect, and also provide a default implementation of the animation.



                  It wasn't a requirement from my original question, but moving this code out of a specific subclass makes this snippet of code reusable (and maintainable) across other subclasses (e.g. UIView, UILabel).



                  protocol ShimmerEffect 
                  var animationDuration: TimeInterval set get
                  var animationDelay: TimeInterval set get

                  var gradientTint: UIColor set get
                  var gradientHighlight: UIColor set get

                  //// Expects value between 0.0—1.0 that represents
                  //// the ratio of the gradient highlight to the full
                  //// width of the gradient.
                  var gradientHighlightRatio: Double set get

                  //// The layer that the gradient will be applied to
                  var gradientLayer: CAGradientLayer get



                  Default implementation:



                  extension ShimmerEffect 

                  /// Configures, and adds the animation to the gradientLayer
                  func addShimmerAnimation()

                  // `gradientHighlightRatio` represents how wide the highlight
                  // should be compared to the entire width of the gradient and
                  // is used to calculate the positions of the 3 gradient colors.
                  // If the highlight is 20% width of the gradient, then the
                  // 'start locations' would be [-0.2, -0.1, 0.0] and the
                  // 'end locations' would be [1.0, 1.1, 1.2]
                  let startLocations = [NSNumber(value: -gradientHighlightRatio), NSNumber(value: -gradientHighlightRatio/2), 0.0]
                  let endLocations = [1, NSNumber(value: 1+(gradientHighlightRatio/2)), NSNumber(value: 1+gradientHighlightRatio)]
                  let gradientColors = [gradientTint.cgColor, gradientHighlight.cgColor, gradientTint.cgColor]

                  // If the gradient highlight ratio is wide, then it can
                  // 'bleed' over into the visible space of the view, which
                  // looks particularly bad if there is a pause between the
                  // animation repeating.
                  // Shifting the start and end points of the gradient by the
                  // size of the highlight prevents this.
                  gradientLayer.startPoint = CGPoint(x: -gradientHighlightRatio, y: 0.5)
                  gradientLayer.endPoint = CGPoint(x: 1+gradientHighlightRatio, y: 0.5)
                  gradientLayer.locations = startLocations
                  gradientLayer.colors = gradientColors

                  let animationKeyPath = "locations"

                  let shimmerAnimation = CABasicAnimation(keyPath: animationKeyPath)
                  shimmerAnimation.fromValue = startLocations
                  shimmerAnimation.toValue = endLocations
                  shimmerAnimation.duration = animationDuration
                  shimmerAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

                  let animationGroup = CAAnimationGroup()
                  animationGroup.duration = animationDuration + animationDelay
                  animationGroup.repeatCount = .infinity
                  animationGroup.animations = [shimmerAnimation]

                  // removes animation with same key (if exists) then adds
                  // the new animation
                  gradientLayer.removeAnimation(forKey: animationKeyPath)
                  gradientLayer.add(animationGroup, forKey: animationKeyPath)




                  In the UIButton subclass I've added property observers to each of the properties that calls addShimmerAnimation() with any property change.



                  I also considered just supplying default values and requiring addShimmerAnimation() to be called manually once properties were configured. Another route was not having any public properties exposed and instead passing everything in through an initializer, but that would remove the possibility of these classes being used in a storyboard (which is an option I like to leave open) and having properties exposed through tagging the properties with IBInspectable.



                  class ShimmerButton: UIButton, ShimmerEffect 

                  override static var layerClass: AnyClass
                  return CAGradientLayer.self


                  var gradientLayer: CAGradientLayer
                  return layer as! CAGradientLayer


                  var animationDuration: TimeInterval = 3
                  didSet addShimmerAnimation()

                  var animationDelay: TimeInterval = 1.5
                  didSet addShimmerAnimation()


                  var gradientHighlightRatio: Double = 0.3
                  didSet addShimmerAnimation()


                  var gradientTint: UIColor = .black
                  didSet addShimmerAnimation()


                  var gradientHighlight: UIColor = .white
                  didSet addShimmerAnimation()


                  override init(frame: CGRect)
                  super.init(frame: frame)
                  gradientLayer.mask = titleLabel?.layer
                  addShimmerAnimation()


                  required init?(coder aDecoder: NSCoder)
                  super.init(coder: aDecoder)
                  gradientLayer.mask = titleLabel?.layer
                  addShimmerAnimation()




                  Example usage:



                  let shimmer = ShimmerButton()
                  shimmer.setTitle("Find new skills", for: .normal)
                  shimmer.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightHeavy)
                  shimmer.gradientTint = darkBlue
                  shimmer.gradientHighlight = lightBlue
                  shimmer.sizeToFit()


                  What I like about this approach is that the complexity is moved out of the subclass making it super easy to duplicate over other views.



                  What frustrates me is that UIView, UILabel and UIButton only have minor differences. I wish there was a way for the computed properties to be extracted into a common place.



                  Example of ShimmerButton and ShimmerView (a UIView subclass) being used together:



                  enter image description here







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Mar 21 '17 at 17:56

























                  answered Mar 20 '17 at 20:51









                  MathewSMathewS

                  568311




                  568311























                      0












                      $begingroup$

                      Shimmer animation can be added like below in iOS



                      class ViewController: UIViewController 
                      @IBOutlet var label: UILabel!
                      let gradientLayer = CAGradientLayer()
                      override func viewDidLoad()
                      super.viewDidLoad()
                      self.gradientLayer.frame = self.label.bounds
                      self.gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
                      self.gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
                      self.gradientLayer.colors = [UIColor.red.cgColor, UIColor.white.cgColor, UIColor.darkGray.cgColor]
                      let startLocations : [NSNumber] = [-1.0,-0.5, 0.0]
                      let endLocations : [NSNumber] = [1.0,1.5, 2.0]

                      self.gradientLayer.locations = startLocations
                      let animation = CABasicAnimation(keyPath: "locations")
                      animation.fromValue = startLocations
                      animation.toValue = endLocations
                      animation.duration = 0.8
                      animation.repeatCount = .infinity

                      self.gradientLayer.add(animation, forKey: animation.keyPath)
                      self.label.layer.addSublayer(self.gradientLayer)

                      DispatchQueue.main.asyncAfter(deadline: .now() + 5)
                      self.gradientLayer.removeAllAnimations()









                      share








                      New contributor




                      Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.






                      $endgroup$

















                        0












                        $begingroup$

                        Shimmer animation can be added like below in iOS



                        class ViewController: UIViewController 
                        @IBOutlet var label: UILabel!
                        let gradientLayer = CAGradientLayer()
                        override func viewDidLoad()
                        super.viewDidLoad()
                        self.gradientLayer.frame = self.label.bounds
                        self.gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
                        self.gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
                        self.gradientLayer.colors = [UIColor.red.cgColor, UIColor.white.cgColor, UIColor.darkGray.cgColor]
                        let startLocations : [NSNumber] = [-1.0,-0.5, 0.0]
                        let endLocations : [NSNumber] = [1.0,1.5, 2.0]

                        self.gradientLayer.locations = startLocations
                        let animation = CABasicAnimation(keyPath: "locations")
                        animation.fromValue = startLocations
                        animation.toValue = endLocations
                        animation.duration = 0.8
                        animation.repeatCount = .infinity

                        self.gradientLayer.add(animation, forKey: animation.keyPath)
                        self.label.layer.addSublayer(self.gradientLayer)

                        DispatchQueue.main.asyncAfter(deadline: .now() + 5)
                        self.gradientLayer.removeAllAnimations()









                        share








                        New contributor




                        Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                        Check out our Code of Conduct.






                        $endgroup$















                          0












                          0








                          0





                          $begingroup$

                          Shimmer animation can be added like below in iOS



                          class ViewController: UIViewController 
                          @IBOutlet var label: UILabel!
                          let gradientLayer = CAGradientLayer()
                          override func viewDidLoad()
                          super.viewDidLoad()
                          self.gradientLayer.frame = self.label.bounds
                          self.gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
                          self.gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
                          self.gradientLayer.colors = [UIColor.red.cgColor, UIColor.white.cgColor, UIColor.darkGray.cgColor]
                          let startLocations : [NSNumber] = [-1.0,-0.5, 0.0]
                          let endLocations : [NSNumber] = [1.0,1.5, 2.0]

                          self.gradientLayer.locations = startLocations
                          let animation = CABasicAnimation(keyPath: "locations")
                          animation.fromValue = startLocations
                          animation.toValue = endLocations
                          animation.duration = 0.8
                          animation.repeatCount = .infinity

                          self.gradientLayer.add(animation, forKey: animation.keyPath)
                          self.label.layer.addSublayer(self.gradientLayer)

                          DispatchQueue.main.asyncAfter(deadline: .now() + 5)
                          self.gradientLayer.removeAllAnimations()









                          share








                          New contributor




                          Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                          Check out our Code of Conduct.






                          $endgroup$



                          Shimmer animation can be added like below in iOS



                          class ViewController: UIViewController 
                          @IBOutlet var label: UILabel!
                          let gradientLayer = CAGradientLayer()
                          override func viewDidLoad()
                          super.viewDidLoad()
                          self.gradientLayer.frame = self.label.bounds
                          self.gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
                          self.gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
                          self.gradientLayer.colors = [UIColor.red.cgColor, UIColor.white.cgColor, UIColor.darkGray.cgColor]
                          let startLocations : [NSNumber] = [-1.0,-0.5, 0.0]
                          let endLocations : [NSNumber] = [1.0,1.5, 2.0]

                          self.gradientLayer.locations = startLocations
                          let animation = CABasicAnimation(keyPath: "locations")
                          animation.fromValue = startLocations
                          animation.toValue = endLocations
                          animation.duration = 0.8
                          animation.repeatCount = .infinity

                          self.gradientLayer.add(animation, forKey: animation.keyPath)
                          self.label.layer.addSublayer(self.gradientLayer)

                          DispatchQueue.main.asyncAfter(deadline: .now() + 5)
                          self.gradientLayer.removeAllAnimations()










                          share








                          New contributor




                          Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                          Check out our Code of Conduct.








                          share


                          share






                          New contributor




                          Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                          Check out our Code of Conduct.









                          answered 1 min ago









                          Alok SInhaAlok SInha

                          1011




                          1011




                          New contributor




                          Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                          Check out our Code of Conduct.





                          New contributor





                          Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                          Check out our Code of Conduct.






                          Alok SInha is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                          Check out our Code of Conduct.



























                              draft saved

                              draft discarded
















































                              Thanks for contributing an answer to Code Review Stack Exchange!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid


                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.

                              Use MathJax to format equations. MathJax reference.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function ()
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f158336%2fuibutton-subclass-with-animated-shimmer-effect%23new-answer', 'question_page');

                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              कुँवर स्रोत दिक्चालन सूची"कुँवर""राणा कुँवरके वंशावली"

                              Why is a white electrical wire connected to 2 black wires?How to wire a light fixture with 3 white wires in box?How should I wire a ceiling fan when there's only three wires in the box?Two white, two black, two ground, and red wire in ceiling box connected to switchWhy is there a white wire connected to multiple black wires in my light box?How to wire a light with two white wires and one black wireReplace light switch connected to a power outlet with dimmer - two black wires to one black and redHow to wire a light with multiple black/white/green wires from the ceiling?Ceiling box has 2 black and white wires but fan/ light only has 1 of eachWhy neutral wire connected to load wire?Switch with 2 black, 2 white, 2 ground and 1 red wire connected to ceiling light and a receptacle?

                              चैत्य भूमि चित्र दीर्घा सन्दर्भ बाहरी कडियाँ दिक्चालन सूची"Chaitya Bhoomi""Chaitya Bhoomi: Statue of Equality in India""Dadar Chaitya Bhoomi: Statue of Equality in India""Ambedkar memorial: Centre okays transfer of Indu Mill land"चैत्यभमि