Sequential fade in Swift - ios

I'm using the excellent answer here to implement a fade in for a text label.
However, I want to introduce a delay so I can sequentially fade in several text labels.
So far (taken from the answer), i'm using :
extension UIView {
func fadeIn(duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: #escaping ((Bool) -> Void) = {(finished: Bool) -> Void in}) {
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.alpha = 1.0
}, completion: completion) }
}
and then implementing with :
override func viewDidLoad() {
self.line1Outlet.alpha = 0
self.line1Outlet.fadeIn(completion: {
(finished: Bool) -> Void in
})
}
I was thinking the best solution would be to implement the delay as a parameter in the extension so I could easily add a different delay to each label. (e.g.
override func viewDidLoad() {
self.line1Outlet.alpha = 0
//add a parameter here for the delay (here line 1 gets '1second' then line 2 could come in after 2seconds etc)
self.line1Outlet.delay = 1second
self.line1Outlet.fadeIn(completion: {
(finished: Bool) -> Void in
})
}
I've tried adding self.delay into the extension (underneath self.alpha) but that doesn't work and I'm not sure how to refactor that extension to allow what I'm after.
The answer to this would then be a reusable method of implementing sequential fades that hopefully would be useful to lots of other people!

In the extension you created, first add self.alpha = 0.0 at the top in fadeIn function, i.e.
extension UIView {
func fadeIn(duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: ((Bool)->())? = nil) {
self.alpha = 0.0
UIView.animate(withDuration: duration, delay: delay, options: .curveEaseIn, animations: {
self.alpha = 1.0
}, completion: completion)
}
}
Now lets assume you've 3 labels in your view, i.e.
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var label3: UILabel!
Add animation to the labels in sequence like,
self.label1.fadeIn(delay: 0.1) { _ in
self.label2.fadeIn(delay: 0.2, completion: { _ in
self.label3.fadeIn(delay: 0.3, completion: { _ in
print("Done All")
})
})
}
Since the duration parameter in fadeIn method is having a default value, we can avoid that.
The way you're calling fadeIn is one way of calling it. Since the method contains multiple default params, it can be called in other ways as well.
Read more about default parameters here.
Edit:
For hiding the labels initially, set the alpha of all labels as 0 in storyboard itself.

Related

Animating UIView isHidden subviews

I have a UIView EmptyCollectionView, which I display when my UICollectionView is empty. The way I have this working is that I create the UIView and addSubview in viewDidLoad of my ViewController, then change toggle isHidden property of the view (as well as the collectionview) as needed.
I'd like to polish things up a little now I have the core function working, and I wan't to add some subtle animation to the subviews contained in my empty view, such as making the contained imageview bounce on display.
So my question is, what is the best way to detect when the UIView is being shown (i.e. is there a viewDidAppear type callback I could use)?
Supplementary question: I'm new to this... Is adding the empty view and toggling the isHidden property a good way of doing this? Or should I be doing it a different way? (i.e. should I instead be creating and destroying the view as needed, rather than keeping it around)
Thanks
The best way in my opinion is to extend UIView
extension UIView {
func fadeIn(_ duration: TimeInterval = 0.2, onCompletion: (() -> Void)? = nil) {
self.alpha = 0
self.isHidden = false
UIView.animate(withDuration: duration,
animations: { self.alpha = 1 },
completion: { (value: Bool) in
if let complete = onCompletion { complete() }
}
)
}
func fadeOut(_ duration: TimeInterval = 0.2, onCompletion: (() -> Void)? = nil) {
UIView.animate(withDuration: duration,
animations: { self.alpha = 0 },
completion: { (value: Bool) in
self.isHidden = true
if let complete = onCompletion { complete() }
}
)
}
}
So you just have to call view.fadeIn() for a default 0.2 sec animation, or view.fadeIn(1) to make it last one second.
You can even add a completion event:
view.fadeOut(0.5, onCompletion: {
print("Animation completed, do whatever you want")
})
This works, I hope it can help you. To hide view:
UIView.animate(withDuration: 0.3/*Animation Duration second*/, animations: {
self.EmptyCollectionView.alpha = 0
}, completion: {
(value: Bool) in
self.EmptyCollectionView.isHidden = true
})
To show view:
self.EmptyCollectionView.isHidden = false
UIView.animate(withDuration: 0.3, animations: {
self.EmptyCollectionView.alpha = 1
}, completion: nil)
Swift 4.2 extension to allow animation when setting isHidden on any UIView class:
extension UIView {
func setIsHidden(_ hidden: Bool, animated: Bool) {
if animated {
if self.isHidden && !hidden {
self.alpha = 0.0
self.isHidden = false
}
UIView.animate(withDuration: 0.25, animations: {
self.alpha = hidden ? 0.0 : 1.0
}) { (complete) in
self.isHidden = hidden
}
} else {
self.isHidden = hidden
}
}
}
You can animate the alpha property of EmptyCollectionView to either 0 to hide or 1 to show
UIView.animate(withDuration: 0.5) {
self.EmptyCollectionView.alpha = 0
}
Also make sure that isOpaque property is set to False to enable Transparency of the view
Swift 5
import UIKit
extension UIView {
func animateSetHidden(_ hidden: Bool, duration: CGFloat = CATransaction.animationDuration(), completion: #escaping (Bool)->() = { _ in}) {
if duration > 0 {
if self.isHidden && !hidden {
self.alpha = 0
self.isHidden = false
}
UIView.animate(withDuration: duration, delay:0, options: .beginFromCurrentState) {
self.alpha = hidden ? 0 : 1
} completion: { c in
if c {
self.isHidden = hidden
}
completion(c)
}
} else {
self.isHidden = hidden
self.alpha = hidden ? 0 : 1
completion(true)
}
}
}

Why are the animated UI Labels in my code starting from within the view?

I'm a very new-to-code learner. I've been taking some online courses, and came across this project recently.
I basically copied the code as I saw it on the screen, as the project files weren't available to download.
The animation is supposed to bring the UILabels from outside the view and position them within the view.
What seems to happen however, is the labels are starting within the view screen and animate outward and beyond.
I've copied the project below. Any help is so very appreciated.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var helloWorld: UILabel!
#IBOutlet weak var secondLabel: UILabel!
#IBOutlet weak var hiddenLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
helloWorld.center.y -= view.bounds.height
secondLabel.center.y += view.bounds.height
hiddenLabel.alpha = 0.0
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Animate Hello World label
UIView.animateWithDuration(1.5, animations: {
self.helloWorld.center.y += self.view.bounds.height
}, completion: { finished in
self.secondAnimation()
})
// Animate background color change
UIView.animateWithDuration(2.0, delay: 1.5, options: [], animations: {
self.view.backgroundColor = UIColor.yellowColor()
}, completion:nil)
}
// Animate second Label
func secondAnimation() {
UIView.animateWithDuration(1.5, delay: 0.0, options: [], animations: { () -> Void in
self.secondLabel.center.y -= self.view.bounds.height
}, completion:nil)
}
func backgroundColor() {
UIView.animateWithDuration(2.5, animations: {
self.view.backgroundColor = UIColor.blackColor()
}, completion: nil)
UIView.animateWithDuration(1.0, delay: 1.5, options: [], animations: {
self.hiddenLabel.alpha = 1.0
}, completion:nil)
}
}
I would check hello world.center.y value in viewWillAppear, before you subtract the view.bounds.height. If center.y value is large and positive, then the subtraction might be setting the center.y value to be inside of the view. If this is the case, then you would want to change the subtraction in viewWillAppear to addition and then the addition in the viewDidAppear animation to subtraction.

How would i go about fading in and out from an array of different words or sentences?

So i built a fade in fade out for a label animation in Swift already. But I want to fade in and out from an array of different sentences, so each time fade in and fade out, it would be different staff.
override func viewDidLoad()
{
super.viewDidLoad()
label.alpha = 0
animatedText()
}
func animatedText(){
UIView.animateWithDuration(2.0, animations: {
self.label.alpha = 1.0
}, completion: {
(Completed: Bool) -> Void in
UIView.animateWithDuration(1.0, delay: 2.0, options: UIViewAnimationOptions.CurveLinear, animations: {
self.label.alpha = 0
}, completion: {
(Completed: Bool) -> Void in
self.animatedText()
})
})
}
Try this function:
class ViewController: UIViewController {
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
var sentences = ["The cat sat.", "The rhino ate.", "The monkey laughed", "The zebra ran.", "The fish swam", "The potato grew."]
override func viewDidLoad() {
super.viewDidLoad()
label1.text = sentences[0]
label2.text = sentences[1]
label1.alpha = 1.0
label2.alpha = 0.0
}
var counter = 1
#IBAction func animateBtnPressed(_ sender: UIButton) {
fadeText(counter: &counter, duration: 2.0)
}
func fadeText(counter: inout Int, duration: TimeInterval) {
if counter < sentences.count {
if counter % 2 != 0 {
UIView.animate(withDuration: duration, animations: {
self.label1.alpha = 0.0
self.label2.alpha = 1.0
}, completion: { finished in
if finished {
counter += 1
if counter <= (self.sentences.count - 1) {
self.label1.text = self.sentences[counter]
} else {
return
}
self.fadeText(counter: &counter, duration: duration)
}
})
} else {
UIView.animate(withDuration: duration, animations: {
self.label1.alpha = 1.0
self.label2.alpha = 0.0
}, completion: { finished in
if finished {
counter += 1
if counter <= (self.sentences.count - 1) {
self.label2.text = self.sentences[counter]
} else {
return
}
self.fadeText(counter: &counter, duration: duration)
}
})
}
}
}
}
Assumptions:
There is an array named sentences (you can alter this) on the ViewController that contains Strings.
There is a countervariable that starts at 1
The UILabels are overlapping in the storyboard.
Other than that you just have to call the function with the counter variable with an & operator in front, because for fadeText to alter the counter variable it needs the address, in memory, of the counter variable, not the value (notice the inout in the parameters of fadeText).
If you have any questions feel free to ask!
I would advise creating an int variable outside the scope of the animatedText() function and simply adding 1 to this variable each time your completion block is called. You can then update the label's text to the string in the array using this int variable as the array's index each time the function is called.

Swift Completely restart the animation on button click

so what I wanna do is to restart the animation, when it's completed and I pressed the Restart button.
Here's what I've got right now.
It starts like that:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var fillView: UIView!
#IBAction func startAnimation(sender: AnyObject) {
self.animationBackground(fillView, animationTime: Float(15))
}
#IBAction func restartAnimation(sender: AnyObject) {
let layerRectangle = fillView.layer
restartLayer(layerRectangle)
}
func animationBackground(view:UIView,animationTime:Float){
UIView.animateWithDuration(NSTimeInterval(animationTime), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
view.transform = CGAffineTransformMakeTranslation(0.0, self.screenSize.height-67)
},completion:nil)
}
func restartLayer(layer:CALayer){
layer.beginTime = 0.0
layer.speed = 0.0
}
}
The restart function works only while animation is playing. But when it's completed that doesn't restart the animation from the beginning. Any tips on how to restart the animation on button click but only when it's already done?
I have already written above.. by the way:
func animationBackground(view:UIView,animationTime:Float){
self.fillView.transform = CGAffineTransformIdentity
UIView.animateWithDuration(NSTimeInterval(animationTime), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
view.transform = CGAffineTransformMakeTranslation(0.0, self.screenSize.height-67)
},completion:nil)
}
You have tranlsated the position and the frame (position). So once the animation has completed you have to restore the frame to its original position. You can restore the frame in completion block of the animation.
here the example:
UIView.animateWithDuration(1, animations: { () -> Void in
self.fillView.transform = CGAffineTransformMakeTranslation(0, 40)
}) { (end) -> Void in
self.fillView.transform = CGAffineTransformIdentity
}
You can you set transform to CGAffineTransformIdentity just before starting the animation.

Unable to Get UIButton to Fade In on ViewDidLoad

As stated in the title, I am unable to get the UIButton to fade in on the ViewDidLoad method. Here is my code thus far:
ViewController.swift
import UIKit
import QuartzCore
class ViewController: UIViewController {
#IBOutlet weak var nextButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
self.nextButton.fadeIn(duration: 10.0, delay: 10.0)
}
}
UIViewExtensions.swift
import Foundation
import UIKit
extension UIView {
func fadeIn(duration: NSTimeInterval = 1.0, delay: NSTimeInterval = 0.0, completion: ((Bool) -> Void) = {(finished: Bool) -> Void in}) {
UIView.animateWithDuration(duration, delay: delay, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.alpha = 1.0
}, completion: completion) }
func fadeOut(duration: NSTimeInterval = 1.0, delay: NSTimeInterval = 0.0, completion: (Bool) -> Void = {(finished: Bool) -> Void in}) {
UIView.animateWithDuration(duration, delay: delay, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.alpha = 0.0
}, completion: completion)
}
}
You do not want to start any animations like a fade in, on the viewDidLoad. This method is called when the class is finished initializing (right after the init). It happens before the view is visible. You want to start animations in the viewDidAppear. This is called once the view is visible on the screen. When you start it in the viewDidLoad, it's already done the animation by the time it gets to the viewDidAppear, assuming of course you did the fade in over about .5 second.
Set you button alpha to 0 on storyboard,so that you can fade in.
Because,if your button alpha is 1,then you want to animate to 1,IOS do nothing
override func viewDidAppear(animated: Bool) {
self.button.fadeIn(duration: 3, delay: 0)
}

Resources