I've created a custom view. added width, height, and centerY constraints.
I used it in a storyboard.
I want to add a label, increase the width animately. and after 1.5 seconds I need to remove the label and decrease the width animatedly.
I wrote the below code inside my custom view.
fileprivate func unfoldMeAnimatedly() {
self.labelLeadingToStatusImageView.isActive = true
self.updateStatusLabel()
self.layoutIfNeeded()
self.constraints.forEach { (constraint) in
if constraint.identifier == "widthOfCardDraftStatus" {
constraint.constant = 95
}
}
UIView.animate(withDuration: 0.6, delay: 0.0, options: UIView.AnimationOptions.curveLinear, animations: {
self.layoutIfNeeded()
}) { (finished) in
}
}
fileprivate func foldMeAnimatedly() {
self.constraints.forEach { (constraint) in
if constraint.identifier == "widthOfCardDraftStatus" {
constraint.constant = 24
}
}
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIView.AnimationOptions.curveLinear, animations: {
//self.statusLabel.alpha = 0.0
self.layoutIfNeeded()
}) { (finished) in
}
}
invoking animations
unfoldMeAnimatedly()
DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
self.foldMeAnimatedly()
}
privew of result:
I did not understand why it is moving. the desired one is just the width needs to be increased/decrease at only one end. i.e right side.
Few observations:
It's strange that I changed the animation durations to 0.5, 0.5 and wait time to 0.5. now it is working as expected.
Why like this? Anybody can explain why it's behaving like this or some issue with code?
Related
I'm trying to hide UIStackView's subview like this:
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2.0,
delay: 0, options: [.curveEaseOut], animations: {
self.label.isHidden = true
self.label.alpha = 0.0
self.stackView.layoutIfNeeded()
})
However, the label disappears instantly with using this code. I suspect this is because of setting isHidden to true, which is required for collapsing.
Is there a way how to hide and collapse UIStackView's subvew with animation? Or it might be better to not to use UIStackView at all?
According to Apple's documentation:
You can animate both changes to the arranged subview’s isHidden property and changes to the stack view’s properties by placing these changes inside an animation block.
I've tested the below code using iOS 12.1 Simulator and it works as expected.
UIView.animate(
withDuration: 2.0,
delay: 0.0,
options: [.curveEaseOut],
animations: {
self.label.isHidden = true
self.label.alpha = 0.0
})
You can animate view properties like alpha, color, etc. However, some things happen instantly - isHidden in this case.
Here's an example using UIView.animate:
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseOut, animations: {
self.label.alpha = 0 // Changes the label's layer alpha value
}, completion: { finished in
self.label.isHidden = true // Hides the label
self.label.layer.alpha = 1 // Resets the label's alpha without un-hiding it
})
Using UIViewPropertyAnimator:
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 0, options: .curveEaseOut, animations: {
self.label.alpha = 0 // Sets the label's alpha
}) { _ in
self.label.isHidden = true // Hides the label
self.label.alpha = 1 // Resets the label's alpha without un-hiding it
}
I have tried your code. Its animating
if self.stackView.subviews.count > 0 {
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 1.0, delay: 0, options: [.curveEaseOut], animations: {
self.stackView.subviews[0].isHidden = true
self.stackView.subviews[0].alpha = 0.0
self.stackView.layoutIfNeeded()
}) { (position) in
self.stackView.subviews[0].removeFromSuperview()
}
}
Just you can use simple solution with animateKeyframes to fade alpha , then hide , i think this will give you what you need So hide after 1 Sec and 0.8 Sec fading
// showLabel is Bool to handle status declare it at you File
#IBAction func toggleStackLabelTapped(_ sender: UIButton) {
showLabel = !showLabel
UIView.animateKeyframes(withDuration: 1, delay: 0, options: .calculationModeLinear, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.8) {
self.label.alpha = (self.showLabel) ? 1 : 0
}
UIView.addKeyframe(withRelativeStartTime: 0.8, relativeDuration: 1) {
self.label.isHidden = !self.showLabel
}
})
}
make sure you have not given height constraint to the stackview.
and try this.
UIView.animate(withDuration: 0.5) {
self.stackView.subviews[INDEX_OF_LABEL_IN_STACK]?.alpha = 0
self.stackView.subviews[INDEX_OF_LABEL_IN_STACK]?.isHidden = true
self.view.layoutSubviews()
}
I have a weird problem that seems to be fairly easy to solve. I think I could use some workaround to be able to have the behavior that I want but I want to know if there is a better way to do it.
I have a view called contentView (blue in the image) that it will expand its height using a UIView.animation, this is no problem at all and works as expected.
The problem here is that this view has a child component (a button) that has an autolayout constraint to its bottom equal to 22 like this:
This is the autolayout constraint:
If I do the height resize without the animation it works fine, the view height change and the button are always 22 points of the bottom of the contentView. But when I use an animation to make this change more smoothy and user-friendly the button moves to the end position before the animation even start.
I want to know how I could achieve a smooth animation here but with the button moving along its parent view
The part of the code that handles the animation is pretty straightforward but I'll post it in here:
#IBAction func openDetail(_ sender: ExpandCourseDetail) {
let rotation = sender.getOpen() ? CGAffineTransform.identity : CGAffineTransform(rotationAngle: CGFloat.pi)
UIView.animate(withDuration: 0.5, delay: 0.1, options: [.curveEaseInOut], animations: {
sender.transform = rotation
}, completion: {
success in
sender.setOpen(!sender.getOpen())
})
UIView.animate(withDuration: 1.0, delay: 0.5, options: [.curveEaseInOut], animations: {
self.contentView.frame.size.height = sender.getOpen() ? self.contentView.frame.height - 300 : self.contentView.frame.height + 300
}, completion: nil)
}
As a side note, the button itself has an animation that rotates the button 180 degrees to show the user that the view is expanding.
Thank you so much for your help.
It's super easy with constraints, just create a superView height constraint IBOutlet and change its constant value.
#IBAction func btnPressed(_ sender: UIButton) {
self.toggleButton.isSelected = !sender.isSelected
//Animation starts here
self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.7) {
if self.toggleButton.isSelected {
//transform button
self.toggleButton.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
//change your ParentView height to desired one
self.constContentViewHeight.constant = self.view.frame.size.height - 220
self.view.layoutIfNeeded()
} else {
self.toggleButton.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi*2))
// Set height constraint to original value
self.constContentViewHeight.constant = 250
self.view.layoutIfNeeded()
}
}
}
I have created a demo, check it out.
The issue you are facing is due to two animation blocks. So I have changed some lines and put both button transformation and height animation into one animation block.
func openDetail(_ sender: ExpandCourseDetail) {
let isOpen = sender.getOpen() ? CGAffineTransform.identity : CGAffineTransform(rotationAngle: CGFloat.pi)
UIView.animate(withDuration: 1.0, delay: 0.5, options: [.curveEaseInOut], animations: {
sender.transform = rotation
self.contentView.frame.size.height = self.contentView.frame.height + (isOpen ? 300 : -300)
}, completion: { (success) in
sender.setOpen(!isOpen)
})
}
I'm trying to animate the height of a UIView from a height of 1px to a height of 41px. It sort of works now, but instead of the view animating from the bottom to the top, it goes from the top to the bottom.
Here is the code that I am using to make it animate up and down
func animateBackgroundHeightUp() {
print("animate")
UIView.animate(withDuration: 0.5, animations: {
self.personBg.frame.size.height = 41
})
}
func animateBackgroundHeightDown() {
print("animate")
UIView.animate(withDuration: 0.5, animations: {
self.personBg.frame.size.height = 1
})
}
I think I have to figure out how to set the origin y, but I can't seem to find a swift 3 example.
EDIT
func animateBackgroundHeightUp() { print("animate") UIView.animate(withDuration: 0.5, animations: { self.personBg.frame.size.height = 41 self.personBg.frame.origin.y = -41 }) }
func animateBackgroundHeightDown() { print("animate") UIView.animate(withDuration: 0.5, animations: { self.personBg.frame.size.height = 1 self.personBg.frame.origin.y = 41 }) }
What ended up working for me is this configuration. But I'm confused as to why the origin needs to be 20?
func animateBackgroundHeightUp() {
print("animate")
UIView.animate(withDuration: 0.25, animations: {
self.personBg.frame.size.height = 41
self.personBg.frame.origin.y = 20
})
}
func animateBackgroundHeightDown() {
print("animate")
UIView.animate(withDuration: 0.25, animations: {
self.personBg.frame.size.height = 1
self.personBg.frame.origin.y = 59.9
})
}
The 59.9 number was figured out by printing out the origin y's value on load.
The origin in iOS views is the top left corner of the screen. A view's frame pins the origin at the top left corner, and the height tells how far it extends down.
If you increase the height of a view, its top won't move - it's bottom will extend downward.
If you want a view to appear to grow upward, you'll need to increase the height while decreasing the origin.y by the same amount.
I have view sView inside my ViewController. It height has constraint - I created IBOutlet for this constraint - sViewHeightConstraint. I want to decrease height of sView with animation.
I created function
UIView.animateWithDuration(5.5, animations: {
self.sViewHeightConstraint.constant = 50
})
Height of view is changing but i don't see any animation. What I am doing wrong?
use layoutIfNeeded()
view.layoutIfNeeded() // force any pending operations to finish
UIView.animateWithDuration(0.2, animations: { () -> Void in
self.sViewHeightConstraint.constant = 50
self.view.layoutIfNeeded()
})
swift 3
view.layoutIfNeeded()
sViewHeightConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
self.view.layoutIfNeeded()
})
In case your view updates but animation isn't working, this solution works for me.
UIView.animate(withDuration: 0.2, animations: {
self.sViewHeightConstraint.constant = 50
self.sView.size = CGSize(width: self.sView.frame.width, height: 50)
self.view.layoutIfNeeded()
})
Inside of these two functions, I want to animate the button shrinking(gone), then animate it growing(seen). I was able to animate it growing, but not at first have the button shrink. Any help on how to animate a button shrinking?
func progressBarButtonFadeOut(){
UIView.animateWithDuration(0.2, animations: {
//timeCapDesign is a UIButton
self.timeCapDesign.transform = CGAffineTransformMakeScale(0, 0)
})
}
//Progress Bar Fade In Buttons
func progressBarButtonFadeIn(){
UIView.animateWithDuration(0.2, animations: {
self.timeCapDesign.transform = CGAffineTransformIdentity
})
}
got it. So what i had to do was this:
func progressBarButtonFadeOut(){
UIView.animateWithDuration(0.2, animations: {
//timeCapDesign is a UIButton
self.timeCapDesign.alpha = 0
self.timeCapDesign.transform = CGAffineTransformMakeScale(0.1, 0.1)
})
}
//Progress Bar Fade In Buttons
func progressBarButtonFadeIn(){
UIView.animateWithDuration(0.2, animations: {
self.timeCapDesign.alpha = 1
self.timeCapDesign.transform = CGAffineTransformIdentity
})
}
So ultimately to shrink it i set it to a value of the CGAffineTransformMakeScale really low such as (0.1,0.1) and then animated the alpha to 0 to give the effect that its shrinking to nothing.
The scale of "1" is the current size, and if you want to animate it growing, the number should be bigger than 1.
func progressBarButtonFadeOut(){
UIView.animateWithDuration(0.2, animations: {
//timeCapDesign is a UIButton
self.timeCapDesign.transform = CGAffineTransformMakeScale(1.5, 1.5)
})
}