19

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?

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
alex
  • 4,804
  • 14
  • 51
  • 86

12 Answers12

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)
     }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Erik Peruzzi
  • 535
  • 1
  • 5
  • 8
  • 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)** – Lance Samaria Aug 29 '20 at 18:58
  • 1
    @LanceSamaria I'm happy that I helped you :) – Erik Peruzzi Aug 30 '20 at 10:38
21

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
Rashwan L
  • 38,237
  • 7
  • 103
  • 107
  • 1
    I couldn't get user interaction to work with the alpha at 0.0. [See this answer for details.](http://stackoverflow.com/a/34297078/1203475) – David L Sep 10 '16 at 21:47
  • @Rashwan L, how can this be modified to flash, lets say 5 times only? – theAlse Dec 02 '16 at 07: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 Dec 02 '16 at 07: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 Dec 02 '16 at 07:24
  • @theAlse, [here](https://drive.google.com/open?id=0B2TR9xoKqvZNRWtZUlhjUDJiOWc) 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 Dec 02 '16 at 07: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) 
Danial Hussain
  • 2,488
  • 18
  • 38
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)
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
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()
        }
    }
}
dungi
  • 302
  • 3
  • 7
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)
    }
}
Dmitry Kuzminov
  • 6,180
  • 6
  • 18
  • 40
Diaa SAlAm
  • 366
  • 1
  • 4
  • 7
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
        })
    }
}
krish
  • 3,856
  • 2
  • 24
  • 28
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   })
black_pearl
  • 2,549
  • 1
  • 23
  • 36
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)
Karen Hovhannisyan
  • 1,140
  • 2
  • 21
  • 31
Vitalii
  • 4,267
  • 1
  • 40
  • 45
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 }
}
Sanf0rd
  • 3,644
  • 2
  • 20
  • 29
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)
ajrlewis
  • 2,968
  • 3
  • 33
  • 67
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
      }
    }
  }
}
Okhan Okbay
  • 1,374
  • 12
  • 26