UIButton shows old title after animation - ios

Can anyone tell me why this shows the old button title for a second when reappearing?
func showSuccess(success:Bool) {
if success == true {
self.stage = 2 //needed to determine what method is triggered on button action
UIView.animateWithDuration(0.4, delay: 0.0, options: .CurveEaseOut, animations: {
self.oldView.alpha = 0
}, completion: { finished in
self.textLabel.text = "new text new text new text"
self.actionButton.setTitle("BAR", forState: .Normal)
self.reappearView(self)
})
}
}
func reappearView(sender:AnyObject) {
self.oldView.layoutIfNeeded()
UIView.animateWithDuration(0.4, delay: 0.0, options: .CurveEaseOut, animations: {
self.oldView.alpha = 1
}, completion: { finished in
})
}
For some odd reason, the text change on the label field works fine as expected. The button will animate in with it's old title, then during the reappear animation - switch to the new title set in the completion handler of showSuccess().
I also tried to move all text changes as first part of reappearView() and then trigger the 2nd animation, same outcome.

Related

UIView Animation Chaining Doesn't Work Properly

I am trying to animate a simple Loading label text to show 3 dots after it, with each dot having a second of delay.
Here is what i tried:
func animateLoading() {
UIView.animate(withDuration: 1, animations: {self.yukleniyorLabel.text = "Yükleniyor."}, completion: { _ in
UIView.animate(withDuration: 1, animations: {self.yukleniyorLabel.text = "Yükleniyor.."}, completion: { _ in
UIView.animate(withDuration: 1, animations: {self.yukleniyorLabel.text = "Yükleniyor..."})
})
})
}
But what i got is the all 3 dots appear in 1 second alltogether. Not in order. See here: https://streamable.com/yiz6s
What am i doing wrong with the chaining? Thanks in advance.
UIView animate is only for animatable view properties such as frame and background color. self.yukleniyorLabel.text is not an animatable property. So you get no animation.
Just use a Timer or delayed performance to change the text at time intervals.
You can use the scheduled Timer for showing text with three dots on the label with animation: ->
var i = 0
var timer : Timer?
loaderLabel.text = "Loading"
timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector:#selector(ViewController.setText), userInfo: nil, repeats: true)
#objc func setText() {
loaderLabel.text = loaderLabel.text! + "."
i += 1
if i >= 3 {
timer?.invalidate()
}
}
output with animation: -> Loading...

switch color directly from highlighted color to another color

I have a UIButton on mainStoryboard. It has white UIColor and orange highlighted color.
I would like to change the color of this button right after the button is selected.
ideal result
White(default) -> orange(highlighted) -> green(animated) -> white(default)
With the following code however, before the color changes from orange to green color it becomes white shortly.
current result
White(default) -> orange(highlighted) -> White(default) -> green(animated) -> white(default)
How can i switch color directly from highlighted orange color to green color?
UIView.animate(withDuration:0, animations: { () -> Void in
cell.buttons[index].backgroundColor = UIColor.green
}) { (Bool) -> Void in
UIView.animate(withDuration: 0.5, animations: { () -> Void in
cell.buttons[index].backgroundColor = UIColor.green
}, completion: { (Bool) -> Void in
UIView.animate(withDuration: 0, animations: { () -> Void in
cell.buttons[index].backgroundColor = UIColor.white
}, completion:nil)
})
}
Your animation code looks okay but animation is a transaction between two states and it need time (duration). So try not to have an animation with duration 0 second because in that case, animation is useless.
Your problem seems to have mistakes made on button listeners. You want to change color to orange as soon as the button is clicked, that is touchDown. Then you want to do color change as soon as the button is released, which is touchUpInside
So try this out, add this code to your viewDidLoad
yourButton.addTarget(self, action:#selector(btnShowPasswordClickHoldDown), for: .touchDown)
yourButton.addTarget(self, action:#selector(btnShowPasswordClickRelease), for: .touchUpInside)
And then add the animation with a valid duration
func btnShowPasswordClickHoldDown(){
UIView.animate(withDuration: 0.5, animations: { () -> Void in
self.yourButton.backgroundColor = UIColor.orange
}, completion:nil)
}
func btnShowPasswordClickRelease(){
UIView.animate(withDuration: 0.5, animations: { () -> Void in
self.yourButton.backgroundColor = UIColor.green
}, completion: { (Bool) -> Void in
UIView.animate(withDuration: 0.5, animations: { () -> Void in
self.yourButton.backgroundColor = UIColor.white
}, completion:nil)
})
}

UITableView beginUpdates endUpdates not working with auto layouts

Here is my screen. Consider the first row till Repeat switch :
Following is the switch action :
/**
Function name : repeatAction
Repeat UISwitch action
**/
#IBAction func repeatAction(sender: UISwitch)
{
if sender.isOn
{
cellDateTime.constrain_viewRepeatHeight.constant = 80
UIView.transition(with: self.cellDateTime.view_repeat, duration: 0.6, options: .transitionCrossDissolve, animations: {() -> Void in
self.cellDateTime.view_repeat.isHidden = false
}, completion: { _ in })
}
else
{
UIView.transition(with: self.cellDateTime.view_repeat, duration: 0.6, options: .transitionCrossDissolve, animations: {() -> Void in
self.cellDateTime.view_repeat.isHidden = true
}, completion: { _ in
self.cellDateTime.constrain_viewRepeatHeight.constant = 0
})
}
let offset = self.table_InitialData.contentOffset
self.table_InitialData.layer.removeAllAnimations()
self.table_InitialData.beginUpdates()
self.table_InitialData.endUpdates()
self.table_InitialData.contentOffset = offset
}
When switch is in on state for the first time, it works fine :
Now when the switch is in off state again, ideally it should appear as in the first screenshot. However, it looks like this :
I am using auto layouts. Just to mention, previously I had the same controls in a simple controller view and that worked fine. But since I placed them in UITableView, it is getting weird.

Unable to remove from superview (swift2)

I am unable to remove from superview with the following code?
Why is this?I have tried everything but seems that it is not working at all.I am adding more details so I don't get this very annoying alert that tells me that my post is mostly code....
let controller = storyboard!.instantiateViewControllerWithIdentifier("OrderViewController")
controller.view.frame = CGRectMake(self.view.frame.size.width/2 - 100, self.view.frame.size.height/2 - 100, 200, 200)
if sender.selected {
sender.selected = false
controller.view.transform = CGAffineTransformIdentity
[UIView .animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
controller.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
}, completion: { finished in
controller.willMoveToParentViewController(nil)
controller.view .removeFromSuperview()
controller.removeFromParentViewController()
})]
print("close")
}
else {
sender.selected = true
addChildViewController(controller)
view.addSubview(controller.view)
controller.didMoveToParentViewController(self)
controller.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView .animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
controller.view.transform = CGAffineTransformIdentity
}, completion: nil)]
print("present")
}
There's a weird mix of objective-c syntax (the square brackets around your UIView animation block) and swift. I'm surprised if this is even compiling without errors!
You're almost there, the main issue is that each time this block of code is called you're instantiating a new instance of an 'OrderViewController'.
You only want to create this if it doesn't exist (when you want to show it). When you're ready to hide it you want to grab a reference to the existing controller and do the animations etc to hide it.
To do that you'll need to keep a reference to it outside of the local scope of that code block.
Here's an example showing how you might do that:
import UIKit
class ViewController: UIViewController {
// keep a reference to the OrderViewController after it's been created
var controller:UIViewController?
#IBAction func buttonTapped(sender: AnyObject) {
// instead of using button selected state, just check to see if
// self.controller is nil or not
if let existingController = self.controller {
// controller already exists, lets hide it
existingController.view.transform = CGAffineTransformIdentity
UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
existingController.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
}, completion: { _ in
existingController.willMoveToParentViewController(nil)
existingController.view.removeFromSuperview()
existingController.removeFromParentViewController()
// make this nil, so that next time the button is
// tapped we'll go though the process of creating it again
self.controller = nil
})
print("close")
}
else {
// controller doesn't exist, lets instanstiate and show it
let newController = storyboard!.instantiateViewControllerWithIdentifier("OrderViewController")
newController.view.frame = CGRectMake(self.view.frame.size.width/2 - 100, self.view.frame.size.height/2 - 100, 200, 200)
addChildViewController(newController)
view.addSubview(newController.view)
newController.didMoveToParentViewController(self)
newController.view.transform = CGAffineTransformMakeScale(0.01, 0.01)
UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
newController.view.transform = CGAffineTransformIdentity
}, completion: { _ in
// keep a reference to this controller you've just created,
// so that next time the button is tapped you can close it
self.controller = newController
})
print("present")
}
}
}
If you're working on a really simple app (just a couple of screens) then this method is okay.
But.... if you're planning on something more complex you might want to investigate using either a custom UISegue, or a mix of UIPresentationController and UIViewControllerAnimatedTransitioning so that all of this animation logic doesn't get locked into your view controller code.

How to cancel previous animation when a new one is triggered?

I am writing a camera app, and have trouble with showing the focus square when user tap on the screen.
My code is (in swift):
self.focusView.center = sender.locationInView(self.cameraWrapper)
self.focusView.transform = CGAffineTransformMakeScale(2, 2)
self.focusView.hidden = false
UIView.animateWithDuration(0.5, animations: { [unowned self] () -> Void in
self.focusView.transform = CGAffineTransformIdentity
}, completion: { (finished) -> Void in
UIView.animateWithDuration(0.5, delay: 1.0, options: nil, animations: { () -> Void in
self.focusView.alpha = 0.0
}, completion: { (finished) -> Void in
self.focusView.hidden = true
self.focusView.alpha = 1.0
})
})
However, if use tap the screen consecutively when the previous animation does not finish, the old and new animation will mix up and the focus view will behave strangely, for example it will disappear very quick.
Could anyone tell me how to cancel previous animation, especially the previous completion block?
You can user method removeAllAnimations to stop animation
Replace your code with below
self.focusView.center = sender.locationInView(self.cameraWrapper)
self.focusView.transform = CGAffineTransformMakeScale(2, 2)
self.focusView.hidden = false
self.focusView.layer.removeAllAnimations() // <<==== Solution
UIView.animateWithDuration(0.5, animations: { [unowned self] () -> Void in
self.focusView.transform = CGAffineTransformIdentity
}, completion: { (finished) -> Void in
UIView.animateWithDuration(0.5, delay: 1.0, options: nil, animations: { () -> Void in
self.focusView.alpha = 0.0
}, completion: { (finished) -> Void in
self.focusView.hidden = true
self.focusView.alpha = 1.0
})
})
Reference : link
#Jageen solution is great, but I worked with UIStackView animation and there I needed additional steps. I have stackView with view1 and view2 inside, and one view should be visible and one hidden:
public func configureStackView(hideView1: Bool, hideView2: Bool) {
let oldHideView1 = view1.isHidden
let oldHideView2 = view2.isHidden
view1.layer.removeAllAnimations()
view2.layer.removeAllAnimations()
view.layer.removeAllAnimations()
stackView.layer.removeAllAnimations()
// after stopping animation the values are unpredictable, so set values to old
view1.isHidden = oldHideView1 // <- Solution is here
view2.isHidden = oldHideView2 // <- Solution is here
UIView.animate(withDuration: 0.3,
delay: 0.0,
usingSpringWithDamping: 0.9,
initialSpringVelocity: 1,
options: [],
animations: {
view1.isHidden = hideView1
view2.isHidden = hideView2
stackView.layoutIfNeeded()
},
completion: nil)
}
In my case, I add the focusing indicator by using addSubview().
self.view.addSubview(square)
square is the UIView I defined in the function. So when the user tap the screen to focus, this function will add a square on subview.
And to cancel this animation when the next tap happen, I just simply use the removeFromSuperview() function, when this function called it removes the view from its superview which is the focusing square here.
filterView.subviews.forEach({ $0.removeFromSuperview() })
It's different from the method above to remove the animation, but remove the subview directly.
In my case, i just needed to set the value, i animated to concrete value.
For e.g.
if ([indexPath isEqual:self.currentPlayerOrderIndexPath])
{
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut
animations:^{
cell.avatarImageV.transform = CGAffineTransformMakeScale(1.15,1.15);
}
completion:NULL];
}
else
{
cell.avatarImageV.transform = CGAffineTransformMakeScale(1.0, 1.0);

Resources