.Repeat .AutoReverse not working - ios

I am using animations to transform a button when it is clicked, I was able to make the button bigger. However, I thought that by using .Repeat and .Autoreverse, the button would go back to its normal state. (scale 1.0) but that is not the case! Maybe I misunderstood the tutorials and questions that I read regarding .AnimateWithDuration ??
This is the code that I am using:
let button = sender as! UIButton
UIView.animateWithDuration(1.0, delay: 0.6,
options: [.Repeat, .Autoreverse, .AllowUserInteraction],
animations:{
button.transform = CGAffineTransformMakeScale(1.2, 1.2)
}, completion: nil)
In another question I saw that the problem may be resolved by adding .AllowUserInteraction but that is not the case.
I don't know if it even matters but this code is enclosed within a touch event.
#IBAction func addButtonClicked(sender: AnyObject) {}
What could be going on here? isn't this how you are supposed to reverse the animation?

At the end of the animation you should reset the size of the object.
The .autoreverse just "reverse visually", but does not change the actual object size.
Try this out.
#IBAction func prss(sender: AnyObject) {
let btt = sender as! UIButton
UIView.animate(withDuration: 1.0, delay: 0.6, options: [.autoreverse, .allowUserInteraction], animations:{
btt.transform = CGAffineTransform(scaleX: 5.0, y: 5.0)
}, completion: { (finished) in
btt.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
})
}

Related

iOS Swift Animation does not work some times

Cant solve problem of showing animation in custom popup.
In my app i use SwiftEntryKit to show top alert with custom view. This custom view contains animation
UIView.animate(
withDuration: 0.8,
delay: 0.0,
options: [.autoreverse, .repeat],
animations:
{
let scale = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.img.transform = scale
}, completion: nil)
Everything works as expected, i can show a popup with scale animation.
But when i call exactly same popup after network call with RxSwift animation does not work. No errors no info, view just gets it scaled up 1.3 size and does not show animation. Here is part of my code with networking
HelperAuth.makeRegister(name: name!, email: email!, password: password!)
.observeOn(MainScheduler.instance)
.subscribe(onNext:
{ reponse in
print(reponse)
}, onError:
{ error in
MessagesManager.showRedAlerter(text: "ERRRRAR")//here is my method to show popup with animation
}).disposed(by: coordinatorAuth.disposeBag)
Cant find any reason of this strange behavior.
As Dhaval Raval mentioned in comments wraping animation in completing block of other animation solves animation problem.
I changed my code of animation to
UIView.animate(
withDuration: 0.0,
animations: {
}, completion:
{ _ in
UIView.animate(
withDuration: 0.8,
delay: 0.0,
options: [.autoreverse, .repeat],
animations:
{
let scale = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.img.transform = scale
}, completion: nil)
})
and now it works as it should and from normal method call and after RxSwift Networking call

Perfect Swift3 Boing

To animate a bar opening...
#IBOutlet var barHeight: NSLayoutConstraint!
barHeight.constant = barShut?30:100
self.view.layoutIfNeeded()
t = !barShut?30:100
UIView.animate(withDuration: 0.15,
delay: 0,
options: UIViewAnimationOptions.curveEaseOut,
animations: { () -> Void in
self.barHeight.constant = t
self.view.layoutIfNeeded()
},
completion: {_ in
Screen.barShut = !Screen.barShut
}
)
That's great ...
But how would you make it boing like this?
(The only way I'd know to do this is, use CADisplayLink, with a few lines of code for a spring decaying.) Is this available in UIKit?
You can use the spring animation method that is built in to UIView:
func toggleBar() -> Void {
self.view.layoutIfNeeded()
let newHeight:CGFloat = !barShut ? 30:100
barShut = !barShut
barHeightConstraint.constant = newHeight
UIView.animate(withDuration: 1.5, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 3, options: [], animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
You will want a longer animation duration than 0.15 of a second in order for the bounce to seem realistic; I think the values I have look pretty good, but you can play with them to get the exact effect you are after.
Since the animation duration is longer, I found that I could tap the button the triggered the open/shut while the previous animation was still running. Setting barShut in the completion block meant that the bar didn't react to all taps. I moved the toggle outside of the animation to address this.

Adding curveEaseIn to Swift Animation

I have an image that rotates but it stops abruptly. I'd like to add curveEaseOut to make it stop smoother, but when I add the animations: .curveEaseOut, I get an error.
func rotateRight () {
let rotation = 90.0
let transform = imageGearRight.transform
let rotated = transform.rotated(by: CGFloat(rotation))
UIView.animate(withDuration: 0.5, animations: .curveEaseOut) {
self.imageGearRight.transform = rotated
}
}
I keep getting an error:
Type '() -> Void' has no member 'curveEaseOut'
I've also tried this code, but I get an error also:
UIView.animate(withDuration: 0.5, animations: UIViewAnimationCurve.easeOut) {
self.imageGearRight.transform = rotated
}
Not sure what I am missing. Any help would be appreciated!!
If you want to specify .curveEaseOut you have to use a UIView method that takes options:
UIView.animate(withDuration: 0.5,
delay: 0,
options: [ .curveEaseOut ],
animations: {
self.imageGearRight.transform = rotated
},
completion: nil)

UIView scale animation overshoots when changed

When I animate a change to a view's transform, then reset that change in another animation before the first animation finishes, everything's great (shown here with a rotation). The animation smoothly switches to the new target:
But when I do this with a scale, the animation overshoots magnificently:
Here's the breaking code:
UIView.animateWithDuration(1) {
self.someView.layer.transform = CATransform3DMakeScale(0.001, 0.001, 1)
}
UIView.animateWithDuration(1,
delay: 0.5,
options: nil,
animations: {
self.someView.layer.transform = CATransform3DIdentity
}, completion: nil
)
Has anyone else seen this? Am I doing something wrong?
EDIT: And is there a good workaround?
EDIT 2: I believe this is a duplicate of this question and am voting to close.
This blog post provides the answer: in iOS 8, UIView animations are additive, and this has an unfortunate result with scale animations.
Basically, the second animation happens together with the first animation. The only solution is to explicitly remove the original animation before starting a new one:
view.layer.transform = view.layer.presentationLayer().transform
view.layer.removeAllAnimations()
Hi I'm not quite sure what your looking for but if you want the view to go back to it's original scale you'd add the .Autoreverse flag.
UIView.animateWithDuration(1, delay: 0, options: .Autoreverse | .Repeat, animations: {
myView.layer.transform = CATransform3DMakeScale(0.001, 0.001, 1)
}, completion: nil)
While if you wanted to string animations together I'd do it within UIView.animateKeyframesWithDuration()
UIView.animateKeyframesWithDuration(2, delay: 0.0, options: nil, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5, animations: {
// Animation 1
})
UIView.addKeyframeWithRelativeStartTime(1, relativeDuration: 0.5, animations: {
// Animation 2
})
}, completion: nil)

Take control of scrollView mid animation of contentOffset

When I tap my scrollView it activates a UITapGestureRecognizer that animates the contentOffset for about 2 seconds. How could I allow the user to interrupt the animation and take full control of the scrollView again when they drag? Right now the user has to wait until the end of the animation to start interacting with the scrollView again.
Note: self refers to the scrollView
Tap set up:
let singleTap = UITapGestureRecognizer(target: self, action: "subtleBounce:")
singleTap.cancelsTouchesInView = false
self.addGestureRecognizer(singleTap)
So when the user taps:
func subtleBounce(gesture : UITapGestureRecognizer) {
let originalFrame = self.frame
UIView.animateWithDuration(0.1, delay: 0.0, usingSpringWithDamping: 1.5, initialSpringVelocity: 1.5, options: UIViewAnimationOptions.CurveLinear, animations: {
self.contentOffset.y -= 10.0
}, completion: {
Void in
UIView.animateWithDuration(0.6, delay: 0.0, usingSpringWithDamping: 0.1, initialSpringVelocity: 3.0, options: UIViewAnimationOptions.CurveLinear, animations: {
self.contentOffset.y += 10.0 }, completion: { Void in
})
})
}
So the code above works as intended.
Here is what I've tried to stop the animation and give the user control of the scrollView again:
let drag = UISwipeGestureRecognizer(target: self, action: "stopScrollAnimation:")
drag.cancelsTouchesInView = false
self.addGestureRecognizer(drag)
and elsewhere:
func stopScrollAnimation(gesture : UISwipeGestureRecognizer) {
self.layer.removeAllAnimations()
self.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false)
}
However this DOES NOT work as intended. The animation still controls the scrollView and the user cannot interact with it.
If you want an example what I mean, look at the iOS7/8 lockscreen. After tapping the screen it also starts an animation. The user can take control of the scrollView mid animation however.
edit: accepting answers in swift or obj-c.
Add AllowUserInteraction to your animation options.

Resources