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)
}
})
}
})
Related
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)
})
}
}
I have having a nightmare with a simple problem. On my app I have clouds moving in the background (currently from left to right).
However with the background they need to be right to left.
Eddited for the more of the page code below. Hopefully this will be easier to work out where I have gone wrong.
#IBOutlet var cloud1: UIImageView!
#IBOutlet var cloud2: UIImageView!
#IBOutlet var cloud3: UIImageView!
#IBOutlet var cloud4: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
cloud1.alpha = 0.0
cloud2.alpha = 0.0
cloud3.alpha = 0.0
cloud4.alpha = 0.0
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud1.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud2.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud3.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud4.alpha = 1.0
}, completion: nil)
animateTheClouds(cloud: cloud1)
animateTheClouds(cloud: cloud2)
animateTheClouds(cloud: cloud3)
animateTheClouds(cloud: cloud4)
}
func animateTheClouds(cloud : UIImageView) {
let cloudMovingSpeed = 60.0/view.frame.size.width
let duration = (view.frame.size.width - cloud.frame.origin.x) * cloudMovingSpeed
UIView.animate(withDuration: TimeInterval(duration), delay: 0.0, options: .curveLinear, animations: {
// Animate the origin to be off the left side of the screen.
cloud.frame.origin.x = cloud.frame.size.width
}, completion: {_ in
// Reset back to the right edge of the screen
cloud.frame.origin.x = -self.view.frame.size.width
self.animateTheClouds(cloud: cloud)
})
If you want to move them from right to left then you simply need to change the starting and ending x origin.
func animateTheClouds(cloud : UIImageView) {
let cloudMovingSpeed = 60.0/view.frame.size.width
let duration = (cloud.frame.origin.x + cloud.frame.size.width) * cloudMovingSpeed
UIView.animate(withDuration: TimeInterval(duration), delay: 0.0, options: .curveLinear, animations: {
// Animate the origin to be off the left side of the screen.
cloud.frame.origin.x = -cloud.frame.size.width
}, completion: {_ in
// Reset back to the right edge of the screen
cloud.frame.origin.x = self.view.frame.size.width
self.animateTheClouds(cloud: cloud)
})
Also make sure the initial x origin is set to self.view.frame.size.width.
My code below is a button that when hit applies animation. Right now there are two animations. I would like to do a sequence the animations meaning have the 2nd animation not start until the first animation has completed.
var speed: CGFloat = 5.3 // speed in seconds
#IBAction func press(_ sender: Any) {
self.theTextView.resignFirstResponder()
UIView.animate(withDuration: TimeInterval(speed), animations: {
////1st action[
self.theTextView.contentOffset = .zero
self.theTextView.setContentOffset(.zero, animated: true)]
/////2nd action[
self.theTextView.contentOffset = CGPoint(x: 0, y: self.theTextView.contentSize.height)]
}, completion: nil)
}}
The easiest way of doing that would be using the completion block of the animate(withDuration:animations:completion:) method. The completion block is executed when the animation sequence ends. In your case, it would look like this :
UIView.animate(withDuration: 6.0, animations: {
// First animation goes here
self.theTextView.contentOffset = CGPoint.zero
}, completion: { (finished) in
// This completion block is called when the first animation is done.
// Make sure the animation was not interrupted/cancelled :
guard finished else {
return
}
UIView.animate(withDuration: 1.0, animations: {
// And the second animation goes here
self.theTextView.contentOffset = CGPoint(x: 0, y: self.theTextView.contentSize.height)
})
})
There is also convenient way to make sequence of concurrent animations by using animateKeyframes:
UIView.animateKeyframes(withDuration: 1, delay: 0, options: .calculationModeCubic, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.3, animations: {
self.someView.alpha = 0.5
self.view.layoutIfNeeded()
})
//second animation
UIView.addKeyframe(withRelativeStartTime: 0.4, relativeDuration: 0.3, animations: {
self.someView.alpha = 1
self.view.layoutIfNeeded()
})
}, completion: { (finish) in
// Do something on animation complete
})
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
})
}
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)
}