Quantcast
Channel: Yudiz Solutions Ltd.
Viewing all articles
Browse latest Browse all 595

UIView Animation with Swift 4

$
0
0

Overview

Out of all the blogs that I wrote, this is a special one. I have come across many animations like interactive, non- interactive, transitions, core-animation, UIView animations, etc. I have googled many third-party libraries and used them too but at the end, dependencies were the obstacles in the way and it is very difficult to modify the library code as per user requirement. So I decided to create few animation methods which can be easily used and modified as per any requirement.

Animation is a better way to represent your application. Animations make app’s user experience more joyful but they are often hard to implement but in this tutorial it will be fun and easy to customize, implement in the app. A good animation shows your skill, potential and a way to handle complex user interface with the better user experience.

The following video shows the demo application.

I’m assuming you are familiar with creating new xcode project, so I’m skipping that part.

Animation Configuration:

Animation requires some configuration, so we are going to create class for that with default values, which are also going to be used for the random animations.

offset: Amount of movement in points will also depend on direction type like (right, left, top, bottom) along with distance.

static var offset: CGFloat = 30.0

duration: Animation duration

static var duration: Double = 0.35

interval: Handling multiple views animation which needs to be animated one after the other and not at the same time

static var interval: Double = 0.075

maxZoomScale: Maximum zoom to be applied in animation.

static var maxZoomScale: Double = 2.0

maxRotationAngle: Maximum rotation to be applied in animation.

static var maxRotationAngle: CGFloat = .pi / 4

AnimationConfiguration class:

//MARK:- AnimationConfiguration
class AnimationConfiguration {

    static var offset: CGFloat = 30.0

    //Duration of the animation.
    static var duration: Double = 0.35

    //Interval for animations handling multiple views that need to be animated one after the other and not at the same time.
    static var interval: Double = 0.075

    static var maxZoomScale: Double = 2.0

    //Maximum rotation (left or right)
    static var maxRotationAngle: CGFloat = .pi / 4

}

Animation Directions:

Animation direction type will identify the flow of animation with possible values which are vertical or horizontal. Depending on the flow of design which is integrated by using UITableView or UICollectionView.

Here I have decided the type of directions that are top, bottom, right and left. Variable declaration of isVertical will check if the animation should go on the X or Y axis and for the isPositive will determine the value is positive or negative.

Random functions will return any random animation direction type.

//MARK:- DirectionType
enum AnimationDirectionType: Int {

    case top
    case bottom
    case right
    case left

    var isVertical: Bool {
        switch self {
        case .top, .bottom:
            return true
        case .left, .right:
            return false
        }
    }

    var isPositive: CGFloat {
        switch self {
        case .top, .left:
            return -1
        case .right, .bottom:
            return 1
        }
    }

    //Random direction.
    static func random() -> AnimationDirectionType {
        let rawValue = Int(arc4random_uniform(4))
        return AnimationDirectionType(rawValue: rawValue)!
    }
}

Animation Type:

Following animation types are available to perform:
from: Animation with direction and offset point.

case from(direction: AnimationDirectionType, offSet: CGFloat)

zoom: Zoom animation.

case zoom(scale: CGFloat)

rotate: Rotation animation.

case rotate(angle: CGFloat)

To create corresponding CGAffineTransform for AnimationType.from user need to declare the variable with return type CGAffineTransform.

Here switch case is used for the self enumeration. Different case handles the different type of animations:
case .from: Allows the direction of animation to be top, bottom, left or right and the offset value of animation from where we need to start.

case .from(direction: let direction, offSet: let offSet):

case .zoom: Take value in scale(CGFLoat) for X and Y axis.

case .zoom(scale: let scale):

case .rotate: Take value in angle(CGFLoat). Rotation will work in left or right direction, it also can be from center or at specific point.

case .rotate(angle: let angle):

You can create a new case or modify the existing one as per the requirement. Play around by changing the point of value to get desired animation.

initialTransform:

var initialTransform: CGAffineTransform {
        switch self {
        case .from(direction: let direction, offSet: let offSet):
            let positive = direction.isPositive
            if direction.isVertical {
                return CGAffineTransform(translationX: 0, y: offSet * postive)
            }
            return CGAffineTransform(translationX: offSet * postive, y: 0)
        case .zoom(scale: let scale):
            return CGAffineTransform(scaleX: scale, y: scale)
        case .rotate(angle: let angle):
            return CGAffineTransform(rotationAngle: angle)
        }
    }

One last method which provide newly generated random animation type.

//Generated random animation.
    static func random() -> AnimationType {
        let index = Int(arc4random_uniform(3))
        if index == 1 {
            return AnimationType.from(direction: AnimationDirectionType.random(),
                                      offSet: AnimationConfiguration.offset)
        } else if index == 2 {
            let scale = Double.random(min: 0, max: AnimationConfiguration.maxZoomScale)
            return AnimationType.zoom(scale: CGFloat(scale))
        }
        let angle = CGFloat.random(min: -AnimationConfiguration.maxRotationAngle, max: AnimationConfiguration.maxRotationAngle)
        return AnimationType.rotate(angle: angle)
    }

UIView extension animation related methods:

Global declaration of completion block

//CompletionBlock
typealias CompletionBlock = (() -> ())

Following animation methods include the parameters:

withType: It’s an array of AnimationType to be performed on block.
reversed: Initial state of the animation. Reverse will start from its original position.
initialAlpha: Initial alpha of the view prior to the animation.
finalAlpha: View’s alpha after the animation.
delay: Time Delay before the animation.
duration: TimeInterval animation will take to complete.
animationInterval: TimeInterval between each of the subviews animations.
backToOriginalForm: View will restore to its identity.
completion: CompletionBlock after the animation finishes.

func animate(withType: [AnimationType], reversed: Bool = false, initialAlpha: CGFloat = 0.0, finalAlpha: CGFloat = 1.0, delay: Double = 0.0, duration: TimeInterval = AnimationConfiguration.duration, backToOriginalForm: Bool = false, completion: CompletionBlock? = nil) {

        let transformFrom = transform
        var transformTo = transform

        withType.forEach { (viewTransform) in
            transformTo = transformTo.concatenating(viewTransform.initialTransform)
        }

        if reversed == false {
            transform = transformTo
        }

        alpha = initialAlpha

        DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
            UIView.animate(withDuration: duration, delay: delay, options: [.curveLinear, .curveEaseInOut], animations: { [weak self] in
                self?.transform = reversed == true ? transformTo : transformFrom
                self?.alpha = finalAlpha
            }, completion: { (_) in
                completion?()
                if backToOriginalForm == true {
                    UIView.animate(withDuration: 0.35, delay: 0.0, options: [.curveLinear, .curveEaseInOut], animations: { [weak self] in
                        self?.transform = .identity
                    }, completion: nil)
                }
            })
        }
    }

Above method will animate to a particular view, subview or contentView but that’s not fun part, we need to think first to perform animation on all views. To perform animation on all subview of main view require some fraction of delay. Animation will work only if there is a delay between subviews, right?

animateAll method require following parameters:
withType: Type of animations.
interval: Interval time of the animation between subviews.

func animateAll(withType: [AnimationType], interval: Double = AnimationConfiguration.interval) {
    for(index, value) in subviews.enumerated() {
       let delay = Double(index) * interval
       value.animate(withType: withType, delay: delay)
    }
}

Now we can animate all views, then what about random animation for all views including subviews or contentviews?

AnimationRandom method require only one parameter:
interval: Interval time of the animation between subviews.

func animationRandom(interval: Double = AnimationConfiguration.interval) {
    for(index, value) in subviews.enumerated() {
       let delay = Double(index) * interval
       let animationRandom = AnimationType.random()
       value.animate(withType: [animationRandom], delay: delay)
    }
}

After creating all animation methods it requires to restore everything back to its identity including subviews. Following method will help you restore the identity.

//It will restore all subview to it's identity
    func restoreAllViewToIdentity() {
        for(_, value) in subviews.enumerated() {
            value.transform = CGAffineTransform.identity
        }
    }

Here are some examples of how to use the code:

Animate all:

let bottomAnimation = AnimationType.from(direction: .bottom, offSet: 30.0)
self.collectionView.animateAll(withType: [bottomAnimation])

Combine animation with completion block:

let zoomOutAnimation = AnimationType.zoom(scale: 0.3)
let angle = AnimationType.from(direction: .bottom, offSet: 30.0)
self.collectionView.animate(withType: [angle, zoomOutAnimation], reversed: true, initialAlpha: 0.0, finalAlpha: 1.0, delay: 0.1, duration: 0.5, backToOriginalForm: true, completion: {
    //
})

Animate main view including the subview with random animation:

view.animationRandom()

Animating cell including content of it:

for subViews in self.tableView.visibleCells {
    let bottomAnimation = AnimationType.from(direction: .bottom, offSet: 30.0)
    subViews.contentView.animateAll(withType: [bottomAnimation])
}

Here is a full source code link. Take a look, dig it, feel free to contribute. Any changes/suggestions are welcome. Please create pull request for contribution.

If you like this type of blog and are interested in future blog of core-animation like pulsating, facebook feed loading animation, or like button animation when facebook live streaming and many more are on the way. Let us know your feedback.

Now you are ready to create your own cool animations.


Viewing all articles
Browse latest Browse all 595

Trending Articles