iOS Label Visibility toggle not animating - ios

I am trying to toggle the visibility of a UILabel based on a Tap Gesture on an UIImageView. The code that performs the toggling is as follows:
func imageTapped(img: UIImageView) {
print(photoTitle.hidden)
if (photoTitle.hidden) {
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.photoTitle.alpha = 1
}, completion: nil)
}
else {
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.photoTitle.alpha = 0
}, completion: nil)
}
self.photoTitle.hidden = !self.photoTitle.hidden
}
The issue with this is that it seems to ignore the animation on the second tap i.e. to hide the UILabel again. It just becomes invisible instead of animating gradually. In the viewdDidLoad(), I initialize the photoTitle.hidden = true to be invisible initially.
Any glaring mistakes?

You need to move self.photoTitle.hidden = true into the completion block of your else condition

hidden doesn't work on this animation, you can instead of alpha

Just try to change the function like this
Swift 2
func imageTapped(img: UIImageView) {
print(photoTitle.hidden)
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.photoTitle.alpha = self.photoTitle.alpha < 0.5 ? 1.0 : 0.0
}, completion: nil)
}
Swift 3, 4, 5
func imageTapped(img: UIImageView) {
print(photoTitle.hidden)
UIView.animate(withDuration: 0.5, delay: 0, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.photoTitle.alpha = self.photoTitle.alpha < 0.5 ? 1.0 : 0.0
}, completion: nil)
}

Related

How can I Use Alpha Extension For All Object in swift?

I have below code for changing the value of object like text,image , ... with Effect. and it work:
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.lblCityTemp.alpha = 0.0
self.imgCityIcon.alpha = 0.0
}, completion: {
(finished: Bool) -> Void in
//Once the label is completely invisible, set the text and fade it back in
self.imgCityIcon.image = UIImage(named: icon)
self.lblCityTemp.text = "\(temp)°"
// Fade in
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.lblCityTemp.alpha = 1.0
self.imgCityIcon.alpha = 1.0
}, completion: nil)
})
Now I want make a extension of this code that I use for all my object that I write below code:
extension NSObject {
func Fade (alphaOUT: Double,alphaIN: Double, input: Any) {
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.alpha = CGFloat(alphaOUT)
}, completion: {
(finished: Bool) -> Void in
// Fade in
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.alpha = CGFloat(alphaIN)
}, completion: nil)
})
}
}
But I have error on self word and I do not know how can I set value for every object like label, imageView , ....
How can I do this?
Is the extension is best way or no?
If no, so what is the best way?
The whole idea should be take some UIView and animate it. Then you need extension of UIView since this class has properties that you need to.
extension UIView { ... }
Anyway, what now if you need to do something when animation ends? Then you'll need completion handler parameter for your method
func fade(..., completion: #escaping () -> Void = { }) {
... which will be called after the first animation ends.
Next suggestions:
You can say that alpha parameters should be of type CGFloat, then you don’t have to convert Double to CGFloat
Also, what is input? I think you won't need it with this solution
Method name should start with small capital letter and for naming parameters you should use camelCase
extension UIView {
func fade(alphaOut: CGFloat, alphaIn: CGFloat, completion: #escaping () -> Void = { }) {
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut, animations: {
self.alpha = alphaOut
}, completion: { _ in
completion() // this is called when animation ends
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.alpha = alphaIn
})
})
}
}
Then when you need to call it, change what you need after animation ends in completion closure
imgCityIcon.fade(alphaOut: ___, alphaIn: ___) {
self.imgCityIcon.image = UIImage(named: icon)
}
Note that you're using old syntax for some stuff:
For example UIViewAnimationOptions.curveEaseIn has been renamed to UIView.AnimationOptions.curveEaseIn. I would suggest you to start using newer versions of Swift
You need to implement an extension for UIView instead of NSObject.
since NSObject doesn't have any property like alpha you will get an
error.
Coding Example:
extension UIView {
func Fade (alphaOUT: Double,alphaIN: Double, input: Any) {
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
self.alpha = CGFloat(alphaOUT)
}, completion: {
(finished: Bool) -> Void in
// Fade in
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseIn, animations: {
self.alpha = CGFloat(alphaIN)
}, completion: nil)
})
}
}
class AnimationHelper {
class func Fade (alphaOUT: Double,alphaIN: Double, input: Any , yourLabel: UILabel, yourTextField: UITextField) {
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
yourLabel.alpha = CGFloat(alphaOUT)
yourTextField.alpha = CGFloat(alphaOUT)
}, completion: {
(finished: Bool) -> Void in
// Fade in
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseIn, animations: {
// Access your text field and label here.
}, completion: nil)
})
}
}

Cannot get closure syntax to work in swift 4

I've tried every syntax variation that I can, but the completion handler always activates before the end of the animation. I think I'm supposed to replace Bool with something else?
UIView.transition(with: swipeForPicturesIndicator,
duration: 1,
options: .curveEaseIn,
animations: {
self.swipeForPicturesIndicator.alpha = 0
},
completion: { (Bool) -> Void in
self.swipeForPicturesIndicator.isHidden = true
self.swipeForPicturesIndicator.alpha = 0.8
})
The Bool value indicates wether the animation was finished when the completion block was called. If that value is false it means your animation was interrupted.
You should use probably animate instead of transition
UIView.animate(withDuration: 1,
delay: 0,
options: .curveEaseIn,
animations: {
self.swipeForPicturesIndicator.alpha = 0
}) { (completed) in
/* Optionally check if animation finished */
self.swipeForPicturesIndicator.isHidden = true
self.swipeForPicturesIndicator.alpha = 0.8
}
Try this
self.swipeForPicturesIndicator.alpha = 0
UIView.transition(with: swipeForPicturesIndicator,
duration: 1,
options: .curveEaseIn,
animations: {
self.swipeForPicturesIndicator.alpha = 0.8
},
completion: nil)
You may want to use animate instead of transition:
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.swipeForPicturesIndicator.alpha = 0
}) { (success) in
self.swipeForPicturesIndicator.isHidden = true
self.swipeForPicturesIndicator.alpha = 0.8
}

UIView.animate not animating bool value perfectly

I tried to animate isHidden, it seems working ok, but if I mistakenly animate isHidden=false 5 times by setting yes to true 5 times, then sometimes I should animate isHidden=true 2 or more time to make my UIView visible!
Am I missing something?
if (yes)
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.isHidden=false
}
}
else
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.isHidden=true
}
}
You should not animate a view's "isHidden" parameter. You should animate its alpha.
if (yes)
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.alpha=1.0
}
}
else
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.alpha=0.0
}
}
-- UPDATE --
If you want to make the view hidden after the animation you can use this:
myLabel.isHidden=false
UIView.animateWithDuration(0.3, delay: 0.0, options: .CurveEaseOut, animations: {
myLabel.alpha=1.0
}, completion: { finished in
})
UIView.animateWithDuration(0.3, delay: 0.0, options: .CurveEaseOut, animations: {
myLabel.alpha=0.0
}, completion: { finished in
myLabel.isHidden=true
})
I think problem is that you are using linear animation on Bool type which has only 2 values (false = 0, true = 1) and any other values between that (it's pulse).
Try this:
if (yes)
{
myLabel.alpha = 0
myLabel.isHidden = false
UIView.animate(withDuration: 0.3, animations: {
myLabel.alpha = 1
})
}
else
{
UIView.animate(withDuration: 0.3, animations: {
myLabel.alpha = 0
}, completion: { (status) in
myLabel.isHidden = true
})
}

(Swift) Fade-In and Out a Label [duplicate]

This question already has answers here:
UIView Hide/Show with animation
(22 answers)
Closed 6 years ago.
I'm trying to fade a label in and out when it appears on the screen. Currently, I'm simply hiding it using
button.hidden = true
and unhiding it by using false. I would like to animate this process with a fade-in and out as it looks much smoother this way. Appreciate the help !
Here is the code I'm using which gets an error. http://imgur.com/a/HI4eg
class ViewController: UIViewController {
#IBOutlet weak var yeah: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseOut, animations: {
self.yeah.alpha = 0.0
}, completion: {
(finished: Bool) -> Void in
//Once the label is completely invisible, set the text and fade it back in
self.yeah.text = "your Text "
// Fade in
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseIn, animations: {
self.yeah.alpha = 1.0
}, completion: {
(finished: Bool) -> Void in
//Once the label is completely invisible, set the text and fade it back in
self.yeah.text = "your Text "
// Fade in
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseIn, animations: {
self.yeah.alpha = 1.0
}, completion:nil )
})
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
As explained in andrew bancroft's blog
// Move our fade out code from earlier
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseOut, animations: {
self.yourLabel.alpha = 0.0
}, completion: {
finished in
if finished {
//Once the label is completely invisible, set the text and fade it back in
self.yourLabel.text = "your Text "
// Fade in
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseIn, animations: {
self.yourLabel.alpha = 1.0
}, completion: nil)
}
})
Modified Answer
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseOut, animations: {
self.yourLabel.alpha = 0.0
}, completion: {
finished in
if finished {
//Once the label is completely invisible, set the text and fade it back in
self.yourLabel.text = "your Text "
// Fade in
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseIn, animations: {
self.yourLabel.alpha = 1.0
}, completion: {
finished in
if finished {
//Once the label is completely invisible, set the text and fade it back in
self.yourLabel.text = "your Text "
// Fade in
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseIn, animations: {
self.yourLabel.alpha = 0.0
}, completion: 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
}
}
}
}

Resources