Swift animateWithDuration() - ios

I want to change the imageView's y in an animation and change the text of the label. But when I do that. The ImageView goes up 100 and then does the animation from -100 to 0. But I want it do go down 100.
If I don't change the text of the label, the animation works perfectly.
label.text = "test"
UIView.animateWithDuration(0.5, animations: {
self.imageView.frame.origin.y += 100
})
Also if I do that, it only changes the text and doesn't move the imageView.
label.text = "test"
imageView.frame.origin.y += 100

You are using AutoLayout. You cannot change the frame directly when using auto layout.
You have to set up the constraints and then animate the change in constraints to animate the views.
Also, you can't do blah.frame.origin.y += 100 IIRC the origin on CGrect is read only.
You can do...
Blah.frame = CGRectOffset(blah.frame, 0, 100)
But again that won't work with auto layout.
Edit
The origin is settable in swift. Thanks jrturton:-)

Related

Change width UIView by animation and center

I have a view and need change width from 100 to 200 by animation.
UIView.animate(withDuration: 5
,animations: {
self.backgroundPlaceHolderView.frame.size.width += 200
})
when running this code, my view change width, but I need a change in width from the center, but this code increase view to the right
If you really need to do it with frames, here's a code snippet:
UIView.animate(withDuration: 1, animations: {
self.backgroudPlaceHolderView.frame.origin.x -= 100
self.backgroudPlaceHolderView.frame.size.width += 200
})
If you moved from manipulating the frame to manipulating constraints this would be easier and cleaner in my opinion.
In the InterfaceBuilder (or programmatically) center your UIView horizontally.
Outlet your width constraint (which would begin at 100).
When you want to increase the width of your UIView you can do the following;
view.layoutIfNeeded() // always make sure any pending layout changes have happened
widthConstraint.constant = 200
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
})
Autolayout will take care of the rest for you. Your view is centered horizontally so it should expand from the center.
You should have that backgroundPlaceHolderView constrained on the centerX. It's probably anchored on the left.

Animate NSLayoutConstraints multiplier and/or isActive state

I want to animate a constraint changing for a custom view.
I've tried ways similar to this:
if widthConstraint.isActive {
widthConstraint.isActive = false
widthConstraintA.isActive = true
} else {
widthConstraintA.isActive = false
widthConstraint.isActive = true
}
UIView.animate(withDuration: 1) {
imageView.layoutIfNeeded()
}
the same for the multiplier (recreating a new constraint like here Can i change multiplier property for NSLayoutConstraint? ). But all states change instantly.
Is there any method that helps to create smooth interpolated animation between two states?
Yes there is suppose that a view1 in storyboard has a width constraint to the main view (self.view in code) like this
view1Width = view * 0.7 + constant
instead of creating 2 constraints and switching between them by changing Active property ORRR [by deactivating current constraint with old multiplier and creating a new one with a new multiplier] leave it 1 constraint and play with it's constant
suppose i want to change view1 multiplier to be 0.9 instead of 0.7
I may do this
self.view1Width.constant += self.view.frame.size.width * 0.2
same you do for subtraction
with this idea you haven't need to be stuck in how to animate active on/off but you still use
self.view.layoutIfNeeded()

How to animate centered square to the top

I have a UIView in a portrait-only app.
The view is centered vertically and horizontally with AutoLayout ("manually" using storyboards).
The width equals the (main)view.width * 0.9
The height is the same size of the width (it is a square).
I want to tap a button inside this UIView and animate it only vertically until it reaches the top border of the screen (eg. height*0.9, 10 pts, whatever is possible).
When I click again, I want to reposition back the view to its original position (centered as it was when I first tapped).
During the transition the square should not be tappable.
After reading many posts I could not understand what's the best way to do this (I red mainly developers saying old techniques using centerX should be avoided and lamentations about some versions of the SO behaving in strange ways).
I suppose I should find a way to get the current "position" of the constraints and to assign a constraint the "final" position, but I was not able to do it.
Any help is appreciated
You are all going the wrong way about this.
Add one constraint that pins the view to the top, and add one constraint that pins the view to centerY. It will complain, so pick one and disable it (I think the property in Interface Builder is called Installed).
If the initial state is the view in the center, disable the constraint that pins it to the top, and viceversa.
Now write IBOutlets for both constraints in your controller and connect them to those constraints. Make sure the declaration of that variable is not weak, otherwise the variable will become nil when the constraint is disabled.
Whenever you want to toggle your animation you can enable one constraint and disable the other.
#IBOutlet var topConstraint: NSLayoutConstraint!
#IBOutlet var centerConstraint: NSLayoutConstraint!
func toggleState(moveToTop: Bool) {
UIView.animateWithDuration(0.25) {
self.topConstraint.isActive = moveToTop
self.centerConstraint.isActive = !moveToTop
self.view.layoutIfNeeded()
}
}
While you can use Autolayout to animate - to take the constraint constraining the centerY and set its constant to a value that would move to the top (e.g., constant = -(UIScreen.main.bounds.height / 2)), I would recommend using view's transform property.
So to move the view to the top you can use:
let topMargin = CGFloat(20)
let viewHalfHeight = self.view.bounds.height / 2
let boxHalfHeight = self.box.bounds.height / 2
UIView.animate(withDuration: 0.2) {
box.transform = CGAffineTransform.identity
.translatedBy(x: 0, y: -(viewHalfHeight - (boxHalfHeight + topMargin)))
}
You are moving box.center related to the view.center - so if you want to move the box to the top, you have to move its center by half a view's height (because the view's centerY is exactly height / 2 far from the view's top). That is not enough though, because then only a bottom half of the box is visible (now the box.centerY == view.top). Therefore you have to move it back by the box.bounds.height / 2 (in my code boxHalfHeight) - to make the top half visible. And to that boxHalfHeight you add topMargin so that there is some margin to the top.
Then, to move the box back to original position:
UIView.animate(withDuration: 0.2) {
box.transform = CGAffineTransform.identity
}
EDIT
If you really want to go with autolayout, you have to have a reference to the centerY constraint, so for example if it is created this way:
let boxCenterYConstraint = self.box.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
boxCenterYConstraint.isActive = true
Then you can try this:
// calculating the translation is the same
let topMargin = CGFloat(20)
let viewHalfHeight = self.view.bounds.height / 2
let boxHalfHeight = self.box.bounds.height / 2
let diff = -(viewHalfHeight - (boxHalfHeight + topMargin))
boxCenterYConstraint.constant = diff
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.2) {
self.view.layoutIfNeeded()
}
And animation back:
boxCenterYConstraint.constant = 0
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.2) {
self.view.layoutIfNeeded()
}

Swift 4 - Editing label frame.size.height with negative number

So I have a label that I've made in Xcode's storyboard which I want to later edit in my code. I want it to simulate something like a vertical bar so I am editing its height by doing:
answerE.frame.size.height = -200
The problem comes from the negative number, I want the label to "grow" up so the height has to be negative from its original position... I have the line in code in a simple action on button press, but each time the line is executed the label moves "up" and eventually after 3-4 clicks is out of the screen.
I just want to edit its height, what is the correct way and what am I doing wrong?
My exact line in code is:
label.frame.size.height = -CGFloat(Double(x)/Double(y) * (200))
If you have added the label in storyboard, why not you use constraints to get the result.
Add leading, trailing , bottom and fixed height constraint and connect IBOutlet to height constraint. Change the constant value of height constraint at the event which you want to perform.
If I'm right, you want the label to gain height, keeping the same bottom edge, but the top edge moving up.
In order to do this, you want to change the frame.origin.y as you change the frame.size.height at the same amount, as its placement (and so top edge) is determined by its origin. So maybe make it zero height, place it where you want it in storyboard, and then when you want it to 'grow' by x:
label.frame.size.height = label.frame.height + x
label.frame.origin.y = label.frame.origin.y - x

UIImageView doesn't animate when using Auto Layout

My image has the following constraints:
leading = 0
trailing = 0
top = 75
height = 250
I want my image to start from the right side of the screen and move to initial place where the constraints are set.
Here is my code:
imageLeadingConstraint.constant = 100
imageTrailingConstraint.constant = -100
imageTopConstraint.constant = 100
image.layoutIfNeeded()
UIView.animate(withDuration: 1.0) {
self.imageLeadingConstraint.constant = 0
self.imageTrailingConstraint.constant = 0
self.imageTopConstraint.constant = 75
self.image.layoutIfNeeded()
}
The issue is that the image is moving to initial place, but not using an animation.
Is my code right ? How come the image just appears in the new position and not animating ?
You should call layoutIfNeeded for imageView's superview.
self.image.superview.layoutIfNeeded()
I'm not sure that I understood you, but:
1) Change constraints (second time) outside of animation block.
2) use self.image.superview?.layoutIfNeeded() instead

Resources