20

I am trying to change a button's color (just a flash/blink) to green when a scan is correct and red when there's a problem. I am able to do this with a view like so

func flashBG(){
    UIView.animateWithDuration(0.7, animations: {
        self.view.backgroundColor = UIColor.greenColor()

    })
}

But with a button it stays green

func flashBtn(){
    UIButton.animateWithDuration(0.5, animations: {
        self.buttonScan.backgroundColor = UIColor.greenColor()
    })
}

I have created the button by code

func setupScanButton() {
    let X_Co = (self.view.frame.size.width - 100)/2
    let Y_Co = (self.viewForLayer.frame.size.height + 36/2)

    buttonScan.frame = CGRectMake(X_Co,Y_Co,100,100)
    buttonScan.layer.borderColor = UIColor.whiteColor().CGColor
    buttonScan.layer.borderWidth = 2
    buttonScan.layer.cornerRadius = 50
    buttonScan.setTitle("Scan", forState: .Normal)
    buttonScan.backgroundColor = UIColor.blueColor()
    buttonScan.addTarget(self, action: "buttonScanAction", forControlEvents: .TouchUpInside)
    buttonScan.setTitleColor(UIColor(red:255/255, green: 255/255, blue:255/255, alpha: 1), forState: UIControlState.Normal)

    self.view.addSubview(buttonScan)
}

Should i call setupScanButton() again?

12 Answers 12

35

This should work in Swift 4

extension UIView{
     func blink() {
         self.alpha = 0.2
         UIView.animate(withDuration: 1, delay: 0.0, options: [.curveLinear, .repeat, .autoreverse], animations: {self.alpha = 1.0}, completion: nil)
     }
}
2
  • 1
    this answer works great! this is how I used it directly on a button (no extension necessary): myButton.alpha = 0.2 UIView.animate(withDuration: 1, delay: 0.0, options: [.curveLinear, .repeat, .autoreverse], animations: {myButton.alpha = 1.0}, completion: nil) Commented Aug 29, 2020 at 18:58
  • 1
    @LanceSamaria I'm happy that I helped you :) Commented Aug 30, 2020 at 10:38
22

This will start and stop a flashing button onClick, if you only want to flash the button immediately just use the first statement.

var flashing = false

@IBAction func btnFlash_Clicked(sender: AnyObject) {
        if !flashing{
            self.buttonScan.alpha = 1.0
            UIView.animateWithDuration(0.5, delay: 0.0, options: [.CurveEaseInOut, .Repeat, .Autoreverse, .AllowUserInteraction], animations: {() -> Void in
                self.buttonScan.alpha = 0.0
                }, completion: {(finished: Bool) -> Void in
            })
            
            flashing = true
        }
    else{
        UIView.animateWithDuration(0.1, delay: 0.0, options: [.CurveEaseInOut, .BeginFromCurrentState], animations: {() -> Void in
            self.buttonScan.alpha = 1.0
            }, completion: {(finished: Bool) -> Void in
        })
    }
}

Swift 5.x version

An updated version with `extension`.
extension UIView {
    func blink(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, alpha: CGFloat = 0.0) {
        UIView.animate(withDuration: duration, delay: delay, options: [.curveEaseInOut, .repeat, .autoreverse], animations: {
            self.alpha = alpha
        })
    }
}

To call the function:

button.blink() // without parameters
button.blink(duration: 1, delay: 0.1, alpha: 0.2) // with parameters

To stop animation: credit to link

func stopBlinking() {
    self.layer.removeAllAnimations()
    alpha = 1
}
5
  • 1
    I couldn't get user interaction to work with the alpha at 0.0. See this answer for details.
    – David L
    Commented Sep 10, 2016 at 21:47
  • @Rashwan L, how can this be modified to flash, lets say 5 times only?
    – theAlse
    Commented Dec 2, 2016 at 7:10
  • @theAlse, declare a variable like: var counter = 0 and then inside of func btnFlash_Clicked make a if statement like if counter > 4 { return } and at the bottom of btnFlash_Clicked just increase the counters value with 1.
    – Rashwan L
    Commented Dec 2, 2016 at 7:15
  • @RashwanL thanks for the reply, I had already tried that without success, there is something fishy going on that I don't undrestand, counter is only increased once and the flashing never ends.
    – theAlse
    Commented Dec 2, 2016 at 7:24
  • @theAlse, here is an example project that I created for you where the button blinks five times. Download the project and you´ll see how this can be done.
    – Rashwan L
    Commented Dec 2, 2016 at 7:42
12

I hope that will solve your problem.

buttonScan.alpha = 1.0
UIView.animate(withDuration: 1.0, delay: 1.0, options: UIView.AnimationOptions.curveEaseOut, animations: {

    buttonScan.alpha = 0.0

}, completion: nil) 
3
  • @DaniSpringer which swift version you are using? Commented Nov 20, 2018 at 8:45
  • 4.2 I’m pretty sure
    – user5306470
    Commented Nov 20, 2018 at 15:31
  • Is button response touches when it is animating ?
    – Hope
    Commented Sep 24, 2021 at 8:55
11

Swift 4:

I've maked an extension with some useful options:

extension UIButton {
    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        return self.bounds.contains(point) ? self : nil
    }
    func blink(enabled: Bool = true, duration: CFTimeInterval = 1.0, stopAfter: CFTimeInterval = 0.0 ) {
        enabled ? (UIView.animate(withDuration: duration, //Time duration you want,
            delay: 0.0,
            options: [.curveEaseInOut, .autoreverse, .repeat],
            animations: { [weak self] in self?.alpha = 0.0 },
            completion: { [weak self] _ in self?.alpha = 1.0 })) : self.layer.removeAllAnimations()
        if !stopAfter.isEqual(to: 0.0) && enabled {
            DispatchQueue.main.asyncAfter(deadline: .now() + stopAfter) { [weak self] in
                self?.layer.removeAllAnimations()
            }
        }
    }
}

First of all, I've overrided the hittest function to enabling the touch also when the button have the alpha equals to 0.0 (transparent) during the animation.

Then , all input vars have a default value so you can launch the blink() method without parameters

I've introduced also the enabled parameter to start or stop the animations on your button.

Finally, if you want you can stop animation after a specific time with the stopAfter parameter.

Usage:

yourButton.blink() // infinite blink effect with the default duration of 1 second

yourButton.blink(enabled:false) // stop the animation

yourButton.blink(duration: 2.0) // slowly the animation to 2 seconds

yourButton.blink(stopAfter:5.0) // the animation stops after 5 seconds.

Typical uses:

yourButton.blink(duration: 1.5, stopAfter:10.0)
// your code..
yourButton.blink()
// other code..
yourButton.blink(enabled:false)
0
8

You can try something like this:

extension UIView {
    func blink() {
        UIView.animateWithDuration(0.5, //Time duration you want,
            delay: 0.0,
            options: [.CurveEaseInOut, .Autoreverse, .Repeat],
            animations: { [weak self] in self?.alpha = 0.0 },
            completion: { [weak self] _ in self?.alpha = 1.0 })
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(2 * NSEC_PER_SEC)),dispatch_get_main_queue()){
            [weak self] in
            self?.layer.removeAllAnimations()
        }
    }
}
2
  • I just removed the view.
    – dungi
    Commented Jul 13, 2016 at 9:03
  • In swift 3+, I do: DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { loadingView.removeFromSuperview() }
    – mmk
    Commented Aug 23, 2019 at 15:46
4
//MARK : Usage 

yourButton.flash()

extension UIButton {

    func flash() {

        let flash = CABasicAnimation(keyPath: "opacity")
        flash.duration = 0.5
        flash.fromValue = 1
        flash.toValue = 0.1
        flash.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        flash.autoreverses = true
        flash.repeatCount = 3

        layer.add(flash, forKey: nil)
    }
}
1

Swift 3.0

func btnFlash_Clicked(sender: AnyObject) {

    if !flashing{
        callButton.alpha = 1.0
        UIView.animate(withDuration: 0.5, delay: 0.0, options: [.allowUserInteraction], animations: {() -> Void in
            callButton.alpha = 0.5
        }, completion: {(finished: Bool) -> Void in
        })

        flashing = true
    }
    else{
        flashing = false
        callButton.alpha = 0.5
        UIView.animate(withDuration: 0.5, delay: 0.0, options: [.allowUserInteraction], animations: {() -> Void in
            callButton.alpha = 1.0
        }, completion: {(finished: Bool) -> Void in
        })
    }
}
1

with UIViewPropertyAnimator and Swift 5

UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 1, delay: 0, options: [.curveLinear,.repeat], animations: {
       UIView.setAnimationRepeatCount(3000)
       self.buttonScan.alpha = 0.0
 }, completion: {_ in   })
0

Swift 3.0

func animateFlash() {        
    flashView.alpha = 0
    flashView.isHidden = false
    UIView.animate(withDuration: 0.3, animations: { flashView.alpha = 1.0 }) { finished in flashView.isHidden = true }
}
0

This UIView extension "blinks" a view and changes the background colour:

/**
 Blinks a view with a given duration and optional color.

 - Parameter duration: The duration of the blink.
 - Parameter color: The color of the blink.
 */
public func blink(withDuration duration: Double = 0.25, color: UIColor? = nil) {

    alpha = 0.2
    UIView.animate(withDuration: duration, delay: 0.0, options: [.curveEaseInOut], animations: {
        self.alpha = 1.0
    })

    guard let newBackgroundColor = color else { return }
    let oldBackgroundColor = backgroundColor

    UIView.animate(withDuration: duration, delay: 0.0, options: [.curveEaseInOut], animations: {
        self.backgroundColor = newBackgroundColor
        self.backgroundColor = oldBackgroundColor
    })

}

You would then use as follows:

buttonScan.blink(color: .green)
0
myButton.alpha = 0.7

UIView.animate(withDuration: 0.3,
                      delay: 1.0,
                    options: [UIView.AnimationOptions.curveLinear, UIView.AnimationOptions.repeat, UIView.AnimationOptions.autoreverse],
                 animations: { myButton.alpha = 1.0 },
                 completion: nil)
2
  • @JobinsJohn try myView.layer.removeAllAnimations()
    – Vitalii
    Commented Apr 13, 2017 at 14:41
  • I just tried what you suggested, but i am not able to doit properly. My basic idea is to run the application for maybe 2 or 3 seconds and then stop it. Commented Apr 16, 2017 at 7:43
0

Another smoothly animating version for Swift 5:

 public extension UIView {
  func blink(duration: TimeInterval) {
    let initialAlpha: CGFloat = 1
    let finalAlpha: CGFloat = 0.2
    
    alpha = initialAlpha
    
    UIView.animateKeyframes(withDuration: duration, delay: 0, options: .beginFromCurrentState) {
      UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
        self.alpha = finalAlpha
      }
      
      UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
        self.alpha = initialAlpha
      }
    }
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.