swift background color animation loop - ios

I want the background color of my iOS app to change between four colors over x amount of seconds
This is what I have so far (it does exactly what I want when I specify just 2 colors)
I also need the animation to run in a loop infinitely.
ViewController.swift
UIView.animateWithDuration(X.0, animations: {
// Color 1
self.view.backgroundColor = UIColor(rgba)
// Color 2
self.view.backgroundColor = UIColor(rgba)
// Color 3
self.view.backgroundColor = UIColor(rgba)
// Color 4
self.view.backgroundColor = UIColor(rgba)
})

Try this out:
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.blackColor()
}) { (Bool) -> Void in
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.greenColor()
}, completion: { (Bool) -> Void in
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.grayColor()
}, completion: { (Bool) -> Void in
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.redColor()
}, completion:nil)
})
})
}
In case you want a continuous repeating animation, try this out:
UIView.animate(withDuration: 2, delay: 0.0, options:[UIView.AnimationOptions.repeat, UIView.AnimationOptions.autoreverse], animations: {
self.view.backgroundColor = UIColor.black
self.view.backgroundColor = UIColor.green
self.view.backgroundColor = UIColor.darkGray
self.view.backgroundColor = UIColor.red
}, completion: nil)

Below code helps to keep enable user interaction with animated view. A random color generation (use in case its needed).
UIView.animateWithDuration(7, delay: 1, options:
[UIViewAnimationOptions.AllowUserInteraction,
UIViewAnimationOptions.Repeat,
UIViewAnimationOptions.Autoreverse],
animations: {
self.yourView.backgroundColor = self.randomColor()
self.yourView.backgroundColor = self.randomColor()
}, completion:nil )
func randomColor() -> UIColor {
let randomRed:CGFloat = CGFloat(drand48())
let randomGreen:CGFloat = CGFloat(drand48())
let randomBlue:CGFloat = CGFloat(drand48())
return UIColor(red: randomRed, green: randomGreen, blue: randomBlue,
alpha: 1.0)
}

You have to use NSTimer:
let timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "update", userInfo: nil, repeats: true)
func update() {
let nextCollor = getNextColor()
UIView.animateWithDuration(X.0, animations: {
self.view.backgroundColor = nextCollor
})
}
func getNextColor() -> UIColor {
let currentColor = self.view.backgroundColor
if currentColor == smaple1 {
return UIColor.redColor()
} else if currentColor == smaple2 {
return UIColor.grayColor()
} else {
return UIColor.whiteColor()
}
}
NSTimer.scheduledTimerWithTimeInterval runs your code every 5 seconds
PS: do not forget invalidate timer when you done with it. Just call timer.invalidate() for it. Otherwise you get a crash.

Swift 5
override func viewWillAppear(_ animated: Bool) {
self.customButton.backgroundColor = .red
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: [.repeat, .autoreverse], animations: {
self.customButton.backgroundColor = .none
}, completion: nil)
}

i had a dynamic / unknown amount of colors to cycle through, so i refactored the Swift 5 answer a bit:
Swift 5
UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse]) {
// colors is the dynamic [UIColor] array previously defined
for color in colors {
self.colorView.backgroundColor = color
}
} completion: { (finished) in
}
this will nicely cycle through the colors with a fade-style animation -- and with the .autoreverse option, it won't "snap" to the starting color.

Related

Swift make background color flash or blink 2 random colors every time the button is pressed

This app currently starts out with a black screen and when the button in the middle is pressed it change the background color every time the button is pressed. But I want the app to blink or flash 2 different colors: not just show one color that is not flashing.
import UIKit
class ViewController: UIViewController {
let colors: [UIColor] = [
.systemYellow,
.systemGreen,
.systemPurple,
.systemPink,
.systemRed,
.systemBlue,
.systemOrange,
.black,.gray
]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
}
#IBAction func didTapButton() {
UIView.animate(withDuration: 1/12, delay:0, options:[ ], animations: {
self.view.backgroundColor = self.colors.randomElement()
self.view.backgroundColor = self.colors.randomElement()
}, completion: nil)
}
}
Try below
UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseOut, animations: {
self.view.backgroundColor = self.colors.randomElement()
}, completion: { finished in
print("another animation! - 1")
UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseOut, animations: {
self.view.backgroundColor = self.colors.randomElement()
}, completion: { finished in
print("another animation! - 2")
})
})
Edit 1
To make forever use below.
func showColor() {
UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseOut, animations: {
self.view.backgroundColor = self.colors.randomElement()
}, completion: { finished in
print("another animation! - 1")
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(showColorAgain), userInfo: nil, repeats: false)
})
}
#objc func showColorAgain() {
showColor()
}
Now to make your logic until next click, you can handle that inside zshowColorAgain` function.
func blink() {
let colors: [UIColor] = [color1, color2, color3, color4...]
runBlink(with: colors)
}
func runBlink(with colors: [UIColor]) {
guard let color = colors.first else { return }
UIView.animate(withDuration: 0.15, delay: 0, options: .curveLinear, animations: {
self.backgroundColor = color
}, completion: { finished in
if finished {
var nextColors = colors
nextColors.removeFirst()
self.runBlink(with: nextColors)
}
})
}

swift how to switch background color when button is pressed

hello I currently have an app that flashes a random color when the button is pressed but it gets stuck in a loop and I can't change to other colors when I press the button again. I would also like to have the message disappear when the button is pressed. thank you so much !!!
import UIKit
class ViewController: UIViewController {
let colors: [UIColor] = [
.systemYellow,
.systemGreen,
.systemPurple,
.systemPink,
.systemRed,
.systemBlue,
.systemOrange,
.black,.gray
]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
}
#IBAction func didTapButton() {
UIView.animate(withDuration: 1/25, delay: 0.0, options:[UIView.AnimationOptions.repeat, UIView.AnimationOptions.autoreverse], animations: {
self.view.backgroundColor = self.colors.randomElement()
self.view.backgroundColor = .black
self.view.backgroundColor = self.colors.randomElement()
}, completion: nil)
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
UIView.animate(withDuration: 1/25, delay: 0.0, options:[UIView.AnimationOptions.repeat, UIView.AnimationOptions.autoreverse], animations: {
self.view.backgroundColor = UIColor.black
self.view.backgroundColor = UIColor.green
self.view.backgroundColor = UIColor.darkGray
self.view.backgroundColor = UIColor.red
self.view.backgroundColor = UIColor.white }, completion: nil) }
}
Try this:
#IBAction func didTapButton()
{
self.view.backgroundColor = UIColor.clear // <-- reset initial background outside animation block
// then start the animation
UIView.animate(withDuration: 1/25, delay: 0.0, options:[.autoreverse, .repeat, .allowUserInteraction], animations: { [weak self] in
self?.view.backgroundColor = self?.colors.randomElement()
}, completion: nil)
}
i.e. just set the backgroundColor to a random color once in the animation block.
Use autoreverse to return the color to the original (clear or transparent in this example).
Use the repeat option to repeat the animation, forever.
Use the allowUserInteraction to allow user interaction during the animation.
"I would also like to have the message disappear when the button is pressed" ... not sure what this is referring to.

Animation stops working when view appears again

extension UIView {
func startBlinking() {
UIView.animate(withDuration: 0.8, delay: 0.0, options: [.allowUserInteraction, .curveEaseInOut, .autoreverse, .repeat], animations: { self.alpha = 0 }, completion: nil)
}
}
How do I use it?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
monthLabel.startBlinking()
}
When method is called first time, then it blinks... but the second time when view did appear the label disappears and... that's it.
Why doesn't it work again?
My logs:
did load
did appear
start blinking
did appear
start blinking
Try:
import UIKit
protocol Then {}
extension Then {
func then(_ block: (Self) -> Void) -> Self {
block(self)
return self
}
}
extension UIView: Then {}
class ViewController: UIViewController {
private var animate: ((Bool) -> Void)?
private var canAnimate: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel().then {
$0.text = "Hello World!"
$0.textColor = .black
$0.translatesAutoresizingMaskIntoConstraints = false
}
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
animate = { [weak self] (forward) in
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 1.0, delay: 0.0, options: [.curveLinear, .autoreverse, .repeat], animations: {
label.alpha = forward ? 0.0 : 1.0
}, completion: { [weak self] _ in
if self?.canAnimate ?? true {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: {
self?.animate?(!forward)
})
}
}).startAnimation()
}
animate?(true)
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
let controller = UIViewController()
self.navigationController?.pushViewController(controller, animated: true)
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
canAnimate = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
canAnimate = true
animate?(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to restart the alpha
func startBlinking() {
self.layer.removeAllAnimations()
self.alpha = 1.0
UIView.animate(withDuration: 0.8, delay: 0.0, options: [.allowUserInteraction, .curveEaseInOut], animations: { self.alpha = 0.0 }, completion: nil)
}
using CABasicAnimation solves my issue
let animation = CABasicAnimation(keyPath: "opacity")
animation.isRemovedOnCompletion = false
animation.fromValue = 1
animation.toValue = 0.1
animation.duration = 0.8
animation.autoreverses = true
animation.repeatCount = Float.infinity
animation.beginTime = 0.0
yourView.layer.add(animation, forKey: nil)

How to make a button flash or blink?

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?
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)
}
}
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
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)
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)
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()
}
}
}
//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)
}
}
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
})
}
}
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 })
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 }
}
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)
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)
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
}
}
}
}

Change label color with animateWithDuration in Swift

I'm trying to animate a label text so that if the value greater it will change the text color to blue and if the value less it will change the color to Red, else remain the same "Black color".
But UIView.animateWithDuration() it change the color to Blue permanently, all I am trying to do is if the value great or less than, I want to change the label color to blue or red for few seconds then return its color to black.
Here is my code:
#IBOutlet weak var label: UILabel!
let x = 10
let y = 20
if x > y
{
UIView.animateWithDuration(2,animations:
{ () -> Void in self.label.textColor = UIColor.blueColor(); })
}
else if y < x
{
UIView.animateWithDuration(2,animations:
{ () -> Void in self.label.textColor = UIColor.redColor(); })
}
else
{
self.label.textColor = UIColor.blackColor()
}
also I tried to use Sleep function as follow but it didn't work out
self.label.textColor = UIColor.blueColor()
sleep(3)
self.label.textColor = UIColor.blackColor()
UIView animation api cannot animate the textColor property of UILabel, for that you will need to use CAAnimation. Here is a implementation using CATransition.
func animate() {
let x = 10
let y = 20
var finalColor: UIColor!
if x > y {
finalColor = UIColor.blueColor()
} else {
finalColor = UIColor.redColor()
}
let changeColor = CATransition()
changeColor.type = kCATransitionFade
changeColor.duration = 2.0
CATransaction.begin()
CATransaction.setCompletionBlock {
self.label.textColor = UIColor.blackColor()
self.label.layer.addAnimation(changeColor, forKey: nil)
}
self.label.textColor = finalColor
self.label.layer.addAnimation(changeColor, forKey: nil)
CATransaction.commit()
}
Building on Acluda's answer, I'd recommend putting his code in the completion handler of the animateWithDuration:animations:completion variant.
UIView.animateWithDuration(2,
animations: { () -> Void in self.label.textColor = UIColor.blueColor(); },
completion: { (wentThrough: Bool) -> Void in
{ UIView.animateWithDuration(2,
animations: { () -> Void in self.label.textColor = UIColor.blackColor(); }) })
UIView.transitionWithView(myLabel, duration: 0.25, options: .TransitionCrossDissolve, animations: {() -> Void in
label.textColor = UIColor.redColor()
}, completion: {(finished: Bool) -> Void in
})
Try :)
There is no logic that tells the UIView to return back to UIColor.blackColor once the first animation is complete.
Consider adding this after animation calls for blue/red.
UIView.animateWithDuration(2,animations:
{ () -> Void in self.label.textColor = UIColor.blackColor(); })

Resources