I have a text field where user should enter info. And a label which points user to text field (like a hint).
I want to stop animation and remove hint label once user presses the text field to enter data.
There is repeating animation on text label. Was created by:
override func viewDidLoad() {
super.viewDidLoad()
textInput.addTarget(self, action: #selector(CalculatorViewController.removeAnimation(_:)), forControlEvents: UIControlEvents.TouchDown)
self.hintLabel.alpha = 0.0
UIView.animateWithDuration(1.5, delay: 0, options: .Repeat
, animations: ({
self.hintLabel.alpha = 1.0
}), completion: nil
)
After it I have created a function to remove annotation
func removeAnimation(textField: UITextField) {
view.layer.removeAllAnimations()
self.view.layer.removeAllAnimations()
print("is it working?!")
}
Should work according to documentation.
My label keeps flashing even though I see the string printed in console. I guess problem is that animation is repeated but have no clue how to resolve this issue.
//Just remove the animation from the label. It will Work
func remove()
{
self.hintLabel.layer.removeAllAnimations()
self.view.layer.removeAllAnimations()
self.view.layoutIfNeeded()
}
Update:
If you want to go nuclear, you could do this, as well:
func nukeAllAnimations() {
self.view.subviews.forEach({$0.layer.removeAllAnimations()})
self.view.layer.removeAllAnimations()
self.view.layoutIfNeeded()
}
Related
Using UITextFieldDelegate I have two function that controls what happens the textfield begins editing and ends.
Using UIView.animate I am having a button disappear and hide using the .isHidden and .alpha property.
.alpha is being used because I also animate the button disappearing with a slight delay.
While the disappearing works well, upon using textFieldDidEndEditing the animation does not work, the button does come back but it does so abruptly with no ease.
To dismiss the keyboard I allow the user to tap anywhere by using this in ViewDidLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target:
self.view, action: #selector(UIView.endEditing(_:))))
The delegate functions I use are below:
extension ExampleViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == exampleTextField {
UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseOut,
animations: {self.exampleButton.alpha = 0.0},
completion: { _ in self.exampleButton.isHidden = true
})
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == exampleTextField {
UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseInOut,
animations: {self.exampleButton.alpha = 1.0},
completion: { _ in self.exampleButton.isHidden = false
//Do anything else that depends on this animation ending
})
}
}
}
Why is the .curveEaseIn not working?
The problem is that the button is hidden. You fade it back in while it is still hidden and then in the completion handler you unhide the button which already has an alpha of 1. This makes it suddenly appear after 0.2 seconds.
Since you are hiding using an alpha of 0 and showing with an alpha of 1, there is no need to set the isHidden property at all in either animation.
I have this simple code:
func tappedButton() {
self.button.alpha = 1.0
UIView.animate(withDuration: 1.0, delay: 4.0, options: .curveLinear, animations: {
self.button.alpha = 0.0
}) { _ in }
}
This function aims at showing a button again for 4 seconds before hiding it (with a 1 second animation). However, while the button is completely visible for these 4 seconds, tapping it doesn't work anymore.
Thanks for your help.
As per the documentation in for the method hittest(_:with:) of UIView https://developer.apple.com/documentation/uikit/uiview/1622469-hittest
This method ignores view objects that are hidden, that have disabled user interactions, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content.
This means that any view, particularly a button, with alpha 0.0 would not be touched.
However, the problem here is that the button is still visible, at least for you. This odd behavior occurs because the actual alpha value of the button is already setted to 0.0 when the animations starts. Animations work by changing the visual hierachy and transition the difference with the parameters you give to the function. In your case, you have two states: a view with a visible button visible and another view without the button. Only the visual part is animated but the corresponding values are already setted. A solution would be:
func tappedButton() {
self.button.alpha = 1.0
UIView.animate(withDuration: 1.0, delay: 4.0, options: .curveLinear, animations: { [weak self] in
self?.button.alpha = 0.01
}) { [weak self] _ in self?.button.alpha = 0.0 }
}
EDIT: This solution seems like a hack but works. I use this approach because the completion handler is always called with a true value.
func tapped() {
let duration = 1.0
let delay = 2.0
let delayDuration = delay + duration
UIView.animate(withDuration: duration, delay: delay, options: [.curveLinear, .allowUserInteraction], animations: { [weak self] in
self?.saveButton.alpha = 0.1
})
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayDuration, execute: { [weak self] in
self?.saveButton.alpha = 0.0
})
}
You need to use allUserInteraction in the options and also check for touches. The animation is added immediately and although you see the button to the system it is already hidden. What does this mean? It means you are watching a movie. But at least with userInteraction enabled you can check for touch events. This is great but how do we know the button is really showing or not? Well you have to use two different checks most likely. One that checks the true UIView alpha of the button and one check that checks the opacity on the presentation layer. I have never fully looked at the link between UIView animations and Core Animation except that I think UIView animations are a wrapper for Core Animations. UIView animations definitely update the view model immediately. So an alpha animation is most likely interpreted into an opacity animation on the layer. Armed with this we can check the opacity of the presentation layer on touches and see that the button is being clicked even if the view model thinks the alpha is 0. This check on the presentation layer will work as long as the opacity is above 0. So here you go.
import UIKit
class ViewController: UIViewController {
lazy var testButton : UIButton = {
let v = UIButton(frame: CGRect(x: 20, y: 50, width: self.view.bounds.width - 40, height: 50))
v.backgroundColor = UIColor.blue
v.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(testButton)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 1.0, delay: 4.0, options: .allowUserInteraction, animations: {
self.testButton.alpha = 0
}, completion: nil)
//test the layer
//test the layer for opacity
if let presentation = testButton.layer.presentation()?.animation(forKey: "opacity"){
print("the animation is already added so normal clicks won't work")
}
}
#objc func buttonClicked(){
print("clicked")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let touch = touches.first{
let location = touch.location(in: self.view)
if self.testButton.frame.contains(location){
//but what you might not know is the animation is probably already running
//and so the check above misses this
if let buttonPres = testButton.layer.presentation(),
let _ = buttonPres.animation(forKey: "opacity"),
let opacity = buttonPres.value(forKey: "opacity") as? CGFloat{
if opacity > 0{
buttonClicked()
}
}
}
}
}
}
I have a view (grey background) and another over it with a UITextField.
When I tap on the grey background, I want the view with the UITextField to disappear with an animation (move to top)
When I tap on the return key (virtual keyboard) the animation is smooth, but when I tap on the grey background, it disappears without animation. When I comment out the vueRecherche.isHidden = true in the completion, it's ok, but I want to hide it!
here is my function
private func fermeRecherche() {
if self.constraintVueRecherche_Top.constant == -5 {
return
}
self.txtRercherche.endEditing(true)
UIView.animate(withDuration: 0.3, animations: {
self.constraintVueRecherche_Top.constant = -5
self.vueFondGris.alpha = 0.0
self.view.layoutIfNeeded()
}, completion: { termine in
self.vueRecherche.isHidden = true
self.vueFondGris.isHidden = true
})
}
with this call, the animation is smooth :
func textFieldDidEndEditing(_ textField: UITextField) {
fermeRecherche()
}
but not with this one :
let tap=UITapGestureRecognizer(target: self, action: #selector(self.tapFondGris(_:)))
vueFondGris.addGestureRecognizer(tap)
func tapFondGris(_ sender: UITapGestureRecognizer) {
fermeRecherche()
}
any ideas?
Thanks
Here is my animation...one weird thing is when I type in a text field and hit done I proceed to set txtdata.text = "".....
and after that my image animation coordinates get all messed up.
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 15.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.smallCloud.center = CGPoint(x: self.screenSize.maxX , y:self.r.origin.y)
}, completion: nil)
} ///////////////////////////////////////////////////////////////
open func textFieldShouldReturn(_ textField: UITextField) -> Bool {
txtData.text = "";
return false
}
so the image basically moves from one side to another...if i dont hit done on the keypad the image moves as expected, however as soon as I hit done and put the UITextField back to " "; the image gets out of wack and goes off the screen anyone have any idea whats going on here?
When I start typing in uitextfield i want some animation to happen and it should not reverse back until i finish typing. I am using this code to animate:
func textFieldDidBeginEditing(textField: UITextField) {
println("begin")
UIView.animateWithDuration(1.0, animations:{
self.secondTextfield.frame = CGRectMake(self.secondTextfield.frame.origin.x + 500, self.secondTextfield.frame.origin.y, self.secondTextfield.frame.size.width, self.venueTextField.frame.size.height)
})
}
What I want to do is: when i start typing in first textfield, i want the second text field to hide out from view and when i finish typing, i want it to animate back in.
What my issue is: When I start typing the animation happens and it comes back to the original position.It doesn't wait for me to finish typing.
Why not change alpha to 0, intead change its frame?
You need to identify witch textfield is editing.
You need to deal with textFieldDidEndEditing to get second textField back.
Example:
func textFieldDidBeginEditing(textField: UITextField) {
if textField == firstTextField {
UIView.animateWithDuration(1.0, animations:{
self.secondTextfield.alpha = 0
})
}
}
func textFieldDidEndEditing(textField: UITextField) {
if textField == firstTextField {
UIView.animateWithDuration(1.0, animations:{
self.secondTextfield.alpha = 1
})
}
}
While not knowing exactly what the problem is, I've found a reasonable alternative to solving your problem:
Instead of animating your frame, animate the auto-layout constraint that defines your textField's x coordinate. For instance, the image below consists of two textFields. I want to animate the bottom one when I begin typing in the top one:
The vertical constraint is a centre x alignment that I want to animate. To achieve the effect, I connected an #IBOutlet to my NSLayoutConstraint:
I had my view controller adopt UITextFieldDelegate and implemented three methods:
extension ViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(textField: UITextField) {
self.c.constant += 100 //c is my constraint
UIView.animateWithDuration(1.0) {
self.view.layoutIfNeeded()
}
}
func textFieldDidEndEditing(textField: UITextField) {
println("textFieldDidEndEditing")
self.c.constant -= 100
UIView.animateWithDuration(1.0) {
self.view.layoutIfNeeded()
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
a.resignFirstResponder()
return true
}
}
This method needs a bit of auto-layout know how, but I was able to achieve the effect you desired.