Core animation (size change) with autolayout challenge - ios

I have a piece of core animation by use of 2 methods in order to give appearance that the object is growing in size from the centre-outwards from the existing location...
(1) viewDidLayoutSubviews()
self.imageView.frame = CGRectMake(187, 249, 0, 0)
which is in the centre of the screen for this imageView object
(2) viewDidAppear()
- with animation block of duration 1 second. e.g.
UIView.animateWithDuration(1.0, delay: 1.5, options: [], animations: {
self.imageView.frame = CGRectMake(16, 70, 343, 358)
}, completion: nil)
which puts object where I want it to show in full size, for the specific screen size.
When I use the x and y coordinates for CGRectMake(...) on the iPhone size I'm looking for they work fine, But when I try on simulator with different screen sizes, it doesn't work in conjunction with the Autolayout constraints, but overrides the existing Constraints.
Question(s)...
Is there a way of making this animation work by centering it proportional to the screen size somehow, rather than explicit coordinates and sizes? via CGRectMake...? (...which seems to show a clash between use of Autolayout and Animation)
Ideally I'm looking for an elegant simple solution? or am I at the limits of the tech?
Many thanks in advance!

You can set imageView location using autolayout constraints & give it a size constraint and by taking an outlet from it you can animate height to increase leaving all other constraints working
#IBOutlet var myViewHeight: NSLayoutConstraint!
UIView.animateWithDuration(0.3, delay: 0.0, options: [], animations:
{
() -> Void in
self.myViewHeight.constant = 300
self.view.layoutIfNeeded()
}, completion:nil)
You should take the outlet from the constraint itself like control+drag on Label.top = topMargin

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.

Animating UIImageView height constraint white space

I'm trying to achieve some kind of "parallax" effect.
Let's say i have an image at the top of the screen, with height: 180 and a scrollview under it.
The initial state is:
state initial
When i scroll up more than 32 px, the image should "dock" up, like making his height 45px, and doing this animated.
The final state should be:
state final
In scrollViewDidEndDragging, i want to dock the image up, animated. I used the following code:
self.imageConstraint?.constant = 45.0
UIView.animate(withDuration: 1.0,
delay: 0,
options: [.beginFromCurrentState,
.allowAnimatedContent,
.allowUserInteraction],
animations: {
self.superview?.layoutIfNeeded()
}, completion: { _ in
completion()
})
The problem is that the image is set to 45 height, but only the image, and a blank space remains ( the initial height-final height ) and that space is animated.
Basically, with no deceleration, when scrollDidEndDragging i want to animate the height of the image and this does not work as expected.
Looks like this:
during animation
What i am doing wrong ?
Write your code in this way it will work hopefully,
UIView.animate(withDuration: 0.5)
{
self. self.imageConstraint.constant = 45.0
self.view.layoutIfNeeded();
}
Do let me if it doesn't. Thanks

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

Button animation doesn't work properly in swift

I'm making an app in swift using Xcode and I'm having troubles with the animation of some button.
Here's my situation:
I have three buttons in the middle of the screen where i've also added some constraints.
What i need is that when the button is clicked it reaches the top of the screen, like in this second photo with an animation:
I've tried this:
UIView.animateWithDuration(1, delay: 0, options: [], animations: {
self.Button2.center.y = 18 + self.Button2.frame.height/2
}, completion: nil)
But what happens is that the button appears from the bottom of the screen slides up to the position of the first photo. It doesn't respect the position I wrote in the animation. Is it possibile that this is due to the constraints? Because I centered it vertically and horizontally.
Can you help me to make it work?
When you work with autolayout and constraints you never make changes to the button frame in your animation, like your code: you must work always with constraints.
This is a generic example:
myButtonCenterYConstraint.constant = 200.0
myButton.layoutIfNeeded()
UIView.animateWithDuration(Double(0.5), animations: {
myButtonCenterYConstraint.constant = 0
myButton.layoutIfNeeded()
})
So, the first step is to declare the button constraints you will want to change as IBOutlets:
First get the center y of the button then reposition the button.
float centerY = self.Button2.center.y;
UIView.animateWithDuration(1, delay: 0, options: [], animations: {
self.Button2.center.y = centerY + 18 + self.Button2.frame.height/2
}, completion: nil)
You must change the constraint first and then update the UI in animation. Something like this.
yourButton.yourOriginConstraint'sYCoordinate.constant = // Set this to what you want to change.
// yes, you should create an IBOutlet for constraint that you want to modify.
UIView.animateWithDuration(1.0, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
Another way is to not user auto layout.
Here, is the reference

how to animate dependent constraints

I'm trying to animate 2 constraints(which determine position on y axis and height) of the same view at same time. The problem is, that they are dependent, by which I mean, they work together to determine the same property. The picture shows what I mean.
2 arrows depict constraints. The upper one sets the distance between upper view and bottom view, the bottom constrains sets the distance between bottom layout guide and bottom view. The bottom view slides from below the view and stops at the position you are currently seeing. The height is set with animating constraints. This is the code I'm using to make animation:
self.toBottomConstraint.constant = 53
self.toUpperConstraint.constant = 15
UIView.animateWithDuration(0.2, delay: 0, usingSpringWithDamping: 20, initialSpringVelocity: 20, options: UIViewAnimationOptions.CurveLinear, animations: {
self.view.layoutIfNeeded()
}, completion: {_ in})
The code actually works but I'm getting this error.
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ...
....
....
....
So how should I animate 2 dependent constraints?
EDIT: //upper view constraints.
// bottom view constraints. BuyButton and collection view are nested inside bottom view
Depending on how you compose your animation, there're literally infinite ways to set the constraints before and after the animation. Most importantly, you need to decide what the constraints were, before the animation, and what they will be after the animation. For example:
self.toBottomConstraint.constant = 153
self.toUpperConstraint.constant = 0
UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
self.toBottomConstraint.constant = 53
self.toUpperConstraint.constant = 15
self.view.layoutIfNeeded()
}, completion: nil)
The code above will animate the bottom view bottom up.
By the way, the console warning message for conflict constraints might be caused by other constraint you set on the bottom view apart from its top and bottom constraints, for example, you might have configured a height constraint for the bottom view, in which case, you need to update the height constraint after the animation.
You're almost there, just move the changing of the constants into the closure and call layoutIfNeeded() earlier too:
self.layoutIfNeeded()
UIView.animateWithDuration(0.2, delay: 0, usingSpringWithDamping: 20, initialSpringVelocity: 20, options: UIViewAnimationOptions.CurveLinear, animations: {
self.toBottomConstraint.constant = 53
self.toUpperConstraint.constant = 15
self.view.layoutIfNeeded()
}, completion: {_ in})
You can read more about animating constraints in another answer about constraint changes.

Resources