Issue #566

Code

Create a container that has blur effect

public class HUDContainer: UIVisualEffectView, AnimationAware {
    private let innerContentView: UIView & AnimationAware
    public let label = UILabel()
    public var text: String? {
        didSet {
            label.text = text
            label.sizeToFit()
            label.isHidden = text == nil
        }
    }

    public init(contentView: UIView & AnimationAware) {
        self.innerContentView = contentView
        super.init(effect: UIBlurEffect(style: .light))
        self.contentView.addSubview(innerContentView)
        self.contentView.addSubview(label)

        innerContentView.pinEdgesToSuperview()
        configure()
    }

    public required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    public func configure() {
        layer.cornerRadius = 8
        layer.masksToBounds = true

        label.isHidden = false
        label.font = UIFont.preferredFont(forTextStyle: .body)
        label.textColor = UIColor.black
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
            label.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10),
            label.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -10),
        ])
    }

    public func startAnimation() {
        innerContentView.startAnimation()
    }

    public func endAnimation() {
        innerContentView.stopAnimation()
    }
}

Make error view with 2 cross lines

import UIKit

public class ErrorView: UIView, AnimationAware {
    public let line1 = CAShapeLayer()
    public let line2 = CAShapeLayer()

    public let animation1 = CASpringAnimation(keyPath: #keyPath(CALayer.transform))
    public let animation2 = CASpringAnimation(keyPath: #keyPath(CALayer.transform))

    public var lineColor: UIColor = UIColor.darkGray
    public var duration: TimeInterval = 0.75

    public override init(frame: CGRect) {
        super.init(frame: frame)
        layer.addSublayer(line1)
        layer.addSublayer(line2)
        configure()
    }

    public required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    public override func layoutSubviews() {
        super.layoutSubviews()

        configureSize()
    }

    public func configure() {
        [line1, line2].forEach {
            $0.backgroundColor = lineColor.cgColor
        }

        [animation1, animation2].forEach {
            $0.fromValue = 0
            $0.damping = 0.33
            $0.initialVelocity = 0.01
            $0.mass = 0.2
            $0.duration = duration
            $0.valueFunction = CAValueFunction(name: CAValueFunctionName.rotateZ)
            $0.timingFunction = CAMediaTimingFunction(name: .easeIn)
        }

        animation1.toValue = CGFloat.pi / 4
        animation2.toValue = -CGFloat.pi / 4
    }

    private func configureSize() {
        guard line1.frame.width <= 0 else {
            return
        }

        [line1, line2].forEach {
            $0.cornerRadius = 3
            $0.frame.size = CGSize(width: bounds.width*0.6, height: 6)
            $0.position = layer.position
        }
    }

    public override func didMoveToWindow() {
        super.didMoveToWindow()

        line1.transform = CATransform3DIdentity
        line2.transform = CATransform3DIdentity
    }

    public func startAnimation() {
        line1.transform = CATransform3DMakeRotation(CGFloat.pi/4, 0, 0, 1.0)
        line2.transform = CATransform3DMakeRotation(-CGFloat.pi/4, 0, 0, 1.0)

        line1.add(animation1, forKey: nil)
        line2.add(animation2, forKey: nil)
    }

    public func stopAnimation() {
        line1.removeAllAnimations()
        line2.removeAllAnimations()
    }
}

Make loading progress using replicator layers

import UIKit

public class ProgressView: UIView, AnimationAware {
    public let replicatorLayer = CAReplicatorLayer()
    public let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
    public let line = CALayer()

    public var lineCount: Int = 12
    public var duration: TimeInterval = 1.0
    public var lineSize: CGSize = CGSize(width: 20, height: 6)
    public var lineColor: UIColor = UIColor.darkGray

    public override init(frame: CGRect) {
        super.init(frame: .zero)

        configure()
    }

    public required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    public func configure() {
        let angle = CGFloat.pi * 2 / CGFloat(lineCount)
        let rotation = CATransform3DMakeRotation(angle, 0, 0, 1.0)

        replicatorLayer.instanceTransform = rotation
        replicatorLayer.instanceCount = lineCount
        replicatorLayer.instanceDelay = duration / TimeInterval(lineCount)

        line.backgroundColor = lineColor.cgColor
        line.frame.size = lineSize
        line.cornerRadius = lineSize.height / 2

        animation.fromValue = 1.0
        animation.toValue = 0.0
        animation.repeatCount = Float.greatestFiniteMagnitude
        animation.timingFunction = CAMediaTimingFunction(name: .linear)
        animation.duration = duration

        replicatorLayer.addSublayer(line)
        layer.addSublayer(replicatorLayer)

        // x: the larger, the closer to center
        // y: half the height, changing affects rotation of lines
        line.position = CGPoint(x: 48, y: 75)
    }

    public override func layoutSubviews() {
        super.layoutSubviews()

        replicatorLayer.frame = bounds
    }

    public func startAnimation() {
        line.add(animation, forKey: nil)
    }

    public func stopAnimation() {
        line.removeAllAnimations()
    }
}

Make success view with check mark animation

import UIKit

public class SuccessView: UIView, AnimationAware {
    public let shapeLayer = CAShapeLayer()
    public let animation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.strokeEnd))

    public var lineColor: UIColor = UIColor.darkGray
    public var duration: TimeInterval = 0.25

    public override init(frame: CGRect) {
        super.init(frame: frame)
        layer.addSublayer(shapeLayer)
        configure()
    }

    public required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    public override func layoutSubviews() {
        super.layoutSubviews()

        configurePath()
    }

    public func configure() {
        shapeLayer.lineCap = .round
        shapeLayer.lineJoin = .round
        shapeLayer.fillColor = nil
        shapeLayer.strokeColor = lineColor.cgColor
        shapeLayer.lineWidth = 6

        animation.timingFunction = CAMediaTimingFunction(name: .easeIn)
        animation.fromValue = 0.0
        animation.toValue = 1.0
        animation.duration = duration
    }

    private func configurePath() {
        let size = CGSize(width: 80, height: 60)
        shapeLayer.frame = CGRect(origin: .zero, size: size)
        shapeLayer.position = layer.position

        let path = UIBezierPath()
        path.move(to: CGPoint(x: size.width * 0, y: size.height * 0.48))
        path.addLine(to: CGPoint(x: size.width * 0.38, y: size.height))
        path.addLine(to: CGPoint(x: size.width, y: size.height * 0.01))

        shapeLayer.path = path.cgPath
    }

    public override func didMoveToWindow() {
        super.didMoveToWindow()

        shapeLayer.strokeEnd = 0.0
    }

    public func startAnimation() {
        shapeLayer.strokeEnd = 1.0
        shapeLayer.add(animation, forKey: nil)
    }

    public func stopAnimation() {
        shapeLayer.removeAllAnimations()
    }
}