How to stop animation block from making uibutton background image flicker - ios

I have two buttons in a view controller that I animate up and then back to their original positions when pressed. 50% of the time there is a discernible gap that appears between the top of the button and it's background image. I think it has to do with using spring/damping but I don't want to not use that. I tried rounding to the nearest whole CGFloat both the button frame and destination coordinates, no luck. Is there a work around to this?
let profileMainScreenButtonAnimateToY = -ceil((profileMainButton.frame.origin.y - 45.0))
let profileStandingsButtonAnimateToY = -ceil((profileStandingsButton.frame.origin.y - 45.0))
UIView.animateWithDuration(0.7, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [.CurveEaseInOut], animations: { () -> Void in
self.profileMainButton.transform = CGAffineTransformMakeTranslation(0.0, profileMainScreenButtonAnimateToY)
self.profileStandingsButton.transform = CGAffineTransformMakeTranslation(0.0, profileStandingsButtonAnimateToY)
}) { (Bool) -> Void in
}
It's difficult to see in the image but there is a yellow horizontal line at the top of the image.

Related

Change UIView's super view with animation in Swift

I have a VideoView (it is a child view of UIView).
By default, it is added to a UIView which is small view in the corner of the screen (I called it ParrentView1).
I have a button to zoom out VideoView. This button performs an action that removes VideoView from ParentView1 and adds it to a bigger view (called ParrentView2).
When I perform the code below, it works but the animation is weird. All I need is a zoom out animation from ParrentView1 to ParrentView2.
Here is my code:
VideoView.removeFromSuperview()
ParrentView2.addSubview(VideoView)
UIView.animate(withDuration: 0.8, delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: .curveEaseOut,
animations: {
VideoView.frame = ParrentView2.bounds
}, completion: nil)
thanks for helping
The likely cause is that when adding it to the other view, it gets assigned a different frame. The solution is to make sure the animation starts at the original location. Something like:
CGRect originalRect = ParrentView2.convert(VideoView.frame, from:VideoView.superView);
VideoView.removeFromSuperview()
ParrentView2.addSubview(VideoView)
VideoView.frame = originalRect;
UIView.animate(withDuration: 0.8, delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: .curveEaseOut,
animations: {
VideoView.frame = ParrentView2.bounds
}, completion: nil)
An improvement point: note that it is customary in Swift to start variable names with a lower case letter. It gets confusing when they don't.

Using UIView.animateKeyframes to animate CAShapeLayer properties

I'm struggling to understand why the following key-framed animation doesn't perform as I expect. Here minuteClock is a subclassed UIView that is also a child view of the main view. minuteClock's horizontal position is set via an exposed constraint. minuteClock's own layer has a CAShapeLayer sublayer called borderLayer.
I'm trying to simultaneously animate the location of minuteClock and the fillcolor of that grandchild borderLayer. What I'm finding is that the keyframes work for the positioning but are ignored for the CAShapeLayer. That is, while the minuteClock moves to 100 half-way through the overall duration, I don't see borderLayer's fillcolor turn red. Instead it seems to just animates very quickly (like a quarter-second) to green.
UIView.animateKeyframes(withDuration: 2.0,
delay: 0,
options: .calculationModeLinear,
animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0,
relativeDuration: 0.5,
animations: {
self.minuteClockPositionConstraint.constant = 100
self.minuteClock.borderLayer.fillColor = UIColor.red.cgColor
self.view.layoutIfNeeded()
})
UIView.addKeyframe(withRelativeStartTime: 0.5,
relativeDuration: 0.5,
animations: {
self.minuteClockPositionConstraint.constant = 50
self.minuteClock.borderLayer.fillColor = UIColor.green.cgColor
self.view.layoutIfNeeded()
})
}, completion: nil)
I think I'm probably going about this wrong, mixing up keyframed animation at the view level with implicit animation triggered by changing a property on a CAShapeLayer.
Any suggestions as to how I should be doing this?

shrink width of button with animation issue

I want to replicate this animation in my project
So what I did try is:
To transform scale X .. but the problem is it also shrinks the title of a button.
self.transform = CGAffineTransform.identity
UIView.animate(withDuration: duration, delay: delayTime,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.7,
options: [.curveEaseIn],
animations: {
self.transform = CGAffineTransform(scaleX: 0.5, y: 1.0)
}, completion: nil)
This is what it gives (title of button also shrinks)
Use of CASpringAnimation
let shrinkAnim = CASpringAnimation(keyPath: "bounds.size.width")
shrinkAnim.damping = 0.7
shrinkAnim.initialVelocity = 0.7
shrinkAnim.fromValue = frame.width
shrinkAnim.toValue = width
shrinkAnim.dura[![enter image description here][3]][3]tion = duration
shrinkAnim.timingFunction = getTimingFunction(curve: curve)
shrinkAnim.fillMode = kCAFillModeForwards
shrinkAnim.isRemovedOnCompletion = false
layer.add(shrinkAnim, forKey: shrinkAnim.keyPath)
So it can change the width but also position of the title
So my question is whats going wrong or what I need to add to replicate first image?
My constraints for button is pinned to left, right and bottom edges and fix height. and more thing is I am making a class for this so I cant change constants because I have to use this in many screens .. so I want one stop solution.
Don't use CGAffineTransform, rather take and NSLayoutConstraint attribute outlet for Button's width and change its constant inside animation block/closure.
UIView.animate(withDuration: duration, delay: delayTime,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.7,
options: [.curveEaseIn],
animations: {
self.buttonWidthConstraint.constant = desired_width_here
}, completion: nil)
Put your button inside a UIView
your view tree would then look like this
Superview > UIView > Button
By doing this you now have a fixed width size your button can follow which is the uiview
It would look like this
And then animate using
//let's say the current left and right constraint are 8
leftConstraint.constant = 50
rightConstraint.constant = 50
UIView.animate ... {
self.view.layoutIfNeeded()
}
By using this you'd only need to set the UIView's frame to your desired frame and have the button follow suit and your animations would just be set proportionate to how you'd code it
//let's say the current left and right constraint are 8
// get view frame
...
// calculate distance
var calculatedDistance = ......
// set distance
let distanceToAnimate = calculatedDistance
leftConstraint.constant = distanceToAnimate
rightConstraint.constant = distanceToAnimate
UIView.animate ... {
self.view.layoutIfNeeded()
}
Have you tried content content hugging and compression resistance. Can you please try doing below at highest priorty.
Apply the transform on the frame of the button. When you scale an UIView in animation it does not take consideration of subviews/layout it will just uniformly scale whatever is being drawn.
self.transform = CGAffineTransform.identity
UIView.animate(withDuration: duration, delay: delayTime,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.7,
options: [.curveEaseIn],
animations: {
self.frame = CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>)
}, completion: nil)

Funky behavior when animating rotation of UIImageView

I'm trying to rotate an UIImageView with the following code:
var x = -((self.needleImage.frame.size.width/2) - 15) //x for rotation point
UIView.animateWithDuration(5.0, delay: 10.0, options: nil, animations: {
var transform = CGAffineTransformMakeTranslation(x, 0)
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
transform = CGAffineTransformTranslate(transform, -x, 0)
self.needleImage.transform = transformm
The end position is right, but during the animation/rotation, the image is shifted a little to the left before settling at the right place.
I tried the same code with a UIView from this and it doesn't do that.
Then I tried wrapping the Imageview inside a View and rotating that, but that didn't help either.
I have drawn a cicle ontop of the rotation point to check if it isn't in the right place, but it seems alright.
I've used this code snippet in the past to rotate a view perpetually, but by removing the last closure it should work to rotate an image 360 degrees. This can be adjusted, or course. Right now it's set to 2PI. You'll have to convert your rotation angle to radians.
func animateRotator() {
UIView.animateWithDuration(0.5, delay: 0, options: .CurveLinear, animations: { () -> Void in
self.rotator.transform = CGAffineTransformRotate(self.rotator.transform, CGFloat(M_PI_2))
}) { (finished) -> Void in
self.animateRotator()
}
}

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)

Resources