UIImageView doesn't animate when using Auto Layout - ios

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

Related

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()
}

Auto Layout how to move the view2 from right to the bottom?

I have two views in the cell at the left UIImageView and at the right UIView Containing some other views as in picture
Now I want if the width of Cell is => 300 then View2 should be at the right of Screen as it is in picture other wise it should be moved to the bottom of View1
Do one thing. Set another constraints for that view
1) leading to main view
2) top to imageview (you can give this constraint by right click from view to drag imageview then vertical spacing.)
Now give outlets for this two constraints programatically.
And also give outlets for constraints which are:
1) leading from view to imageview
2) top of view.
Now as per your requirement,set if condition
e.g.
if(width => 300)
{
topOfViewToImageviewConstraint.isActive = false
leadingOfViewToMainViewConstraint.isActive = false
}
else
{
leadingOfViewToImageViewConstraint.isActive = false
topOfviewToMainView.isActive = false
leadingOfViewToMainViewConstraint.constant = 0
topOfViewToImageviewConstraint.constant = 20
}
This will work. I am not currently in Xcode, so this is just some sample code, but the idea will work.
in cellForRowAt
check if self.tableView.cellForRowAt(IndexPath.row).frame.width => 300
if it is, make no change, but if it is not find the y coordinate of the bottom of view 1 and make a new rect at (x: 0, y: botOfView1, width, height). Then set the frame of view 2 equal to that.

Popup View with Header-TableView-Footer and AutoLayout Not Working

Context:
I have a pop up view that contains a tableview. I would like the pop up view to be less than the whole height of the screen but would like the tableview to scale with the number of rows it has. (If the table view contains 2 rows then it would fit those 2 rows and would not scroll, if the table view has 30 rows it will max out at the height of the screen and allow scrolling).
View Hierarchy:
- viewContainer (clear background, whole view)
-- viewPopUp (pop up container)
--- labelHeader
--- tableView
--- buttonOK
Constraints:
viewPopUp.centerY = viewContainer.centerY
viewPopUp.centerX = viewContainer.centerX
viewPopUp.leadingSpaceTo superView = 32 #750
viewPopUp.trailingSpaceTo superView = 32 #750
viewPopUp.width &lt= 300
viewPopUp.height &lt= 0.8 * viewContainer.height
labelHeader.topSpaceTo superView = 0
labelHeader.leadingSpaceTo superView = 0
labelHeader.trailingSpaceTo superView = 0
labelHeader.bottomSpaceTo tableView = 0
labelHeader.height = 33
tableView.topSpaceTo labelHeader = 0
tableView.leadingSpaceTo superView = 0
tableView.trailingSpaceTo superView = 0
tableView.bottomSpaceTo buttonOk = 0
tableView.height &gt= 0
tableView.height = 0 #200
buttonOk.topSpaceTo tableView = 0
buttonOK.leadingSpaceTo superView = 0
buttonOK.trailingSpaceTo superView = 0
buttonOk.bottomSpaceTo superview = 0
buttonOK.height = 35
My Question:
In my UIViewController's viewDidLoad I know exactly how many rows will need to be represented in this tableView and I would like to add an additional constraint to have the tableView scale to this number as described in the context above.
I have tried tableView.height = count * rowHeight #500 but this does not update the view. I am not sure if I am not calling something like needsUpdateConstraints or needsUpdateLayout etc.
If I add the above constraint as required then it will update the tableView as expected but I receive constraint warnings and I believe this will break if it pushes the tableView past the size of the screen which is why I wanted to add it as a non-required constraint so it will drop off after it reaches the screen height.
Does anyone have any ideas on how I can reach this desired outcome?
Thanks!
Assuming that the height of the rows are always the same, you could calculate the height of the table, check it against the view height and set this value to a referenced height constraint added to the table view.
The code would look something like:
//calculate the height
var tableHeight = numberOfRows * rowHeight
if tableheight > maxHeight {
tableHeight = maxHeight
}
self.tableHeightConstraint.constant = tableHeight //this constraint should be a iboutlet
self.view.layoutIfNeeded()
Hope it helps.

Swift animateWithDuration()

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:-)

Resources