(Swift)
I recently noticed a button on my viewController was not clickable because of the animation that is infinitely assigned to it. However, I really need to be able to click that button.
override func viewDidLoad() {
super.viewDidLoad()
self.startGradientFunction()
}
// Function that changes colors over time
func startGradientFunction() {
UIView.animate(withDuration: 2.5, animations: { () -> Void in
self.view.backgroundColor = self.hexStringToUIColor(hex: "66B8FF")
self.readyBtn.backgroundColor = self.hexStringToUIColor(hex: "3F86C3")
}, completion: { (finished: Bool) in
UIView.animate(withDuration: 2.5, animations: { () -> Void in
self.view.backgroundColor = self.hexStringToUIColor(hex: "57FFC4")
self.readyBtn.backgroundColor = self.hexStringToUIColor(hex: "20CD91")
}, completion: { (finished: Bool) in
UIView.animate(withDuration: 2.5, animations: { () -> Void in
self.view.backgroundColor = self.hexStringToUIColor(hex: "9C49FF")
self.readyBtn.backgroundColor = self.hexStringToUIColor(hex: "7E3BCD")
}, completion: { (finished: Bool) in
self.startGradientFunction()
})
})
})
}
How can I make it selectable again?
I would be glad to know someone can help me.
You need to add user interaction to your animation options:
https://developer.apple.com/documentation/uikit/uiview/animationoptions/1622440-allowuserinteraction
(But you should also fix your way of doing a repeating animation so that you don’t recurse infinitely.)
Related
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.
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)
}
}
}
I'm having some trouble with the animations in a tabbed app in Xcode.
I have the viewDidLoad and the viewDidAppear parts, the problem is that I have two labels label1 and label2. I would like label1 appearing only once when the application loads, and label2 appearing every time I go back to the FirstView.
So the logical thing to do would be:
override func viewDidLoad(animated: Bool) {
super.viewDidLoad()
self.label1.alpha = 0
self.label2.alpha = 0
//this is the animation
UIView.animateWithDuration(2.0, animations: { () -> Void in
self.label1.alpha = 1.0
//this is what happens after a delay
[DELAY CODE]
self.label1.alpha = 0.0
})
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
UIView.animateWithDuration(2.0, animations: { () -> Void in
self.label2.alpha = 1.0
}
Essentially what this should do is make label1 appear and disappear only once, and make label2 appear every time firstView shows up on the screen. The problem is that I have an error in the first line telling me "Method does not override any method from its superclass". So how can I do what I am trying to achieve?
You have to remove the animated:Bool from your viewDidLoad-method. there is no such parameter in this method.
So it should look like that:
override func viewDidLoad() {
Try This :
UIView.animateWithDuration(2.0,
animations: { () -> Void in
// Your animation method
self.label1.alpha = 1.0
}) { (Bool) -> Void in
// Call your delay method here.
// Delay - 3 seconds
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(3),
target: self,
selector: "hideLabel",
userInfo: nil,
repeats: false)
}
//
func hideLabel {
UIView.animateWithDuration(2,
animations: { () -> Void in
self.label1.alpha = 0.0
})
}
I want to perform some animation on secondTextField when the FirstTextfield becomesFirstResponder
After resign first responder the animation should reverse back in second text field. How can I achieve this in swift?
Note: It is different from moving textfield to the top on keyboard appears.
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
if(textField == firstTextField) {
UIView.animateWithDuration(1.0, animations: {
self.secTextField.frame = CGRectMake(self.secTextField.frame.origin.x + 500, self.secTextField.frame.origin.y, self.secTextField.frame.size.width, self.secTextField.frame.size.height) })
}
return true
}
What I want to do is, when the user types in firstTextField, the secTextField should go and hide out of sight.
What actually happens is the secTextField comes from out of sight to its original position
You can use the delegate methods of UITextField to achieve this. If you explain more about your animation I can help you with that also.
func textFieldDidBeginEditing(textField: UITextField) {
if textField == self.firstTextField {
UIView.animateWithDuration(2, delay: 0, options: nil, animations: { () -> Void in
self.secondTextField.frame = CGRectOffset(self.secondTextField.frame, 500, 0)
}, completion: nil)
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == self.firstTextField {
UIView.animateWithDuration(2, delay: 0, options: nil, animations: { () -> Void in
self.secondTextField.frame = CGRectOffset(self.secondTextField.frame, -500, 0)
}, completion: nil)
}
textField.resignFirstResponder()
return true
}
Don't forget to set delegates on the UITextField objects (on your viewDidLoad)
self.firstTextField.delegate = self
self.secondTextField.delegate = self
You can download the example from here: https://github.com/alextarrago/text-field-test
In my case it works for me. Just use self.view.layoutIfNeeded()
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
if(textField == firstTextField) {
UIView.animateWithDuration(1.0, animations: {
self.secTextField.frame = CGRectMake(self.secTextField.frame.origin.x + 500, self.secTextField.frame.origin.y, self.secTextField.frame.size.width, self.secTextField.frame.size.height)
self.view.layoutIfNeeded()
})
}
return true
}
I finally found the answer.
I did the following steps.
Clean My project,
Enable and then again disable the Auto-Layouts.
Set the delegate for those textfields both in xib and swift file.
Then the coding:
`self.secTextField.frame.origin.x = self.secTextField.frame.origin.x + 500`
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)
}