Animations using PinLayout
## Basic Swift animations
Before explaining how to use PinLayout to animate views, you can check these two nice tutorials to learn the basic of animations:
* [iOS Animation Tutorial: Getting Started](https://www.raywenderlich.com/363-ios-animation-tutorial-getting-started)
* [Basic UIView Animation Tutorial: Getting Started](https://www.raywenderlich.com/5255-basic-uiview-animation-tutorial-getting-started)
## PinLayout is stateless
It is important to remember that PinLayout is stateless, i.e. PinLayout always start from the view's current position and size (frame) when layouting a view. You don't need to reset anything to animate a view using PinLayout.
This also means that you can modify only the property you want to animate, for example the following code will only animate the view's width:
```
UIView.animate(withDuration: 0.3) {
view.pin.width(30)
}
```
## Layout strategies
Multiple strategies can be used to animate layout using PinLayout. The choice is a question of preferences and the kind of animations you want to achieve.
Some possible strategies will be shown below using a simple example. The example animates a view position from left to right when the user tap a button.
Note that in the following source code the view's size was set to 150 px (`view.pin.size(150)`) in the initialization.
### Basic strategy: Using `UIView.setNeedsLayout` and `UIView.layoutIfNeeded`
In this strategy, to force a call to layoutSubviews(), we call `UIView.setNeedsLayout` and `UIView.layoutIfNeeded` from the animation block.
```swift
var isViewLeftDocked = true
override func layoutSubviews() {
super.layoutSubviews()
if isViewLeftDocked {
view.pin.top().left()
} else {
view.pin.top().right()
}
}
func didTapTogglePosition() {
isViewLeftDocked = !isViewLeftDocked
UIView.animate(withDuration: 0.3) {
self.setNeedsLayout()
self.layoutIfNeeded()
}
}
```
### Using a layout method
This strategy use a private method to layout the animated view (`layoutAnimatedView()`). The advantage of this solution is that it is not required to call `UIView.setNeedsLayout` and `UIView.layoutIfNeeded` to relayout the view.
```swift
var isViewLeftDocked = true
override func layoutSubviews() {
super.layoutSubviews()
layoutAnimatedView()
}
private func layoutAnimatedView() {
if isViewLeftDocked {
view.pin.top().left()
} else {
view.pin.top().right()
}
}
func didTapTogglePosition() {
isViewLeftDocked = !isViewLeftDocked
UIView.animate(withDuration: 0.3) {
self.layoutAnimatedView()
}
}
```
### Using an animation state
This strategy is similar to the previous one, but use an enumeration to keep the animation state.
```swift
enum AnimationState {
case leftDocked
case rightDocked
}
var animationState = AnimationState.leftDocked
override func layoutSubviews() {
super.layoutSubviews()
layoutAnimatedView()
}
private func layoutAnimatedView() {
switch animationState {
case .leftDocked:
view.pin.top().left()
case .rightDocked:
view.pin.top().right()
}
}
func didTapTogglePosition() {
switch animationState {
case .leftDocked: animationState = .rightDocked
case .rightDocked: animationState = .leftDocked
}
UIView.animate(withDuration: 0.3) {
self.layoutAnimatedView()
}
}
```
### Other strategies
It's really up to you to think of animation's strategies that match your situation. With PinLayout you are always in control of everything, including animations.
## Collision between animations and `layoutSubViews()`
In some particular situation it is possible that `layoutSubViews()` may be called during the animation is in progress, this can occur particularly on long animation. To handle this kind of situation it is possible to use a boolean indicating if an animation is in progress, and to block temporarely the layout of animated views in `layoutSubViews()`.
Here is an example:
```swift
enum AnimationState {
case leftDocked
case rightDocked
}
var animationState = AnimationState.leftDocked
var isAnimating = false
override func layoutSubviews() {
super.layoutSubviews()
// If an animation of the view is in progress, we don't update animated views position.
guard !isAnimating else { return }
layoutAnimatedView()
}
private func layoutAnimatedView() {
switch animationState {
case .leftDocked:
view.pin.top().left()
case .rightDocked:
view.pin.top().right()
}
}
func didTapTogglePosition() {
switch animationState {
case .leftDocked: animationState = .rightDocked
case .rightDocked: animationState = .leftDocked
}
UIView.animate(withDuration: 0.3, animations: {
self.isAnimating = true
self.layoutAnimatedView()
}, completion: { (_) in
self.isAnimating = false
})
}
```
## Animation Example
You can check the animation example available in the [PinLayout's Example App](https://github.com/layoutBox/PinLayout/blob/master/docs/examples.md):
[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/Animations/AnimationsView.swift)