Skip to content

Instantly share code, notes, and snippets.

@ABashkirova
Created May 18, 2021 20:01
Show Gist options
  • Save ABashkirova/cba0ba89ebdfb0f856c1e15e347cf191 to your computer and use it in GitHub Desktop.
Save ABashkirova/cba0ba89ebdfb0f856c1e15e347cf191 to your computer and use it in GitHub Desktop.
Instagram Story Loading Animation
import UIKit
class ViewControllerWithLoader : UIViewController {
var fromColor: UIColor = UIColor(red: 138.0 / 255, green: 59.0 / 255, blue: 184.0 / 255, alpha: 1)
var toColor: UIColor = UIColor(red: 205.0 / 255, green: 71.0 / 255, blue: 107.0 / 255, alpha: 1)
var ringWidth: CGFloat = 8
var duration: TimeInterval = 4.5
override func loadView() {
let rootView = UIView(
frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 300.0)
)
rootView.backgroundColor = .white
self.view = rootView
let frame = rootView.frame
let ringLayer = ringLayer(frame: frame.insetBy(dx: 50.0, dy: 50.0))
rootView.layer.addSublayer(ringLayer)
let avatarLayer = avatarLayer(frame: frame.insetBy(dx: 58.0, dy: 58.0))
rootView.layer.addSublayer(avatarLayer)
let loadAnimation = loadingAnimation()
loadAnimation.repeatCount = .greatestFiniteMagnitude
ringLayer.add(loadAnimation, forKey: nil)
}
func avatarLayer(frame: CGRect) -> CAShapeLayer {
let avatar = CAShapeLayer()
avatar.frame = frame
if let avatarImage = "🎡".toImage(size: frame.size, color: .systemBlue.withAlphaComponent(0.3)) {
avatar.contents = avatarImage.cgImage
print(avatarImage.size)
}
avatar.masksToBounds = true
avatar.cornerRadius = avatar.frame.height / 2
avatar.strokeColor = UIColor.white.cgColor
avatar.lineWidth = 2
return avatar
}
func ringLayer(frame: CGRect) -> CAShapeLayer {
let ringLayer = CAShapeLayer()
ringLayer.frame = frame
ringLayer.path = UIBezierPath(ovalIn: ringLayer.bounds).cgPath
ringLayer.fillColor = UIColor.clear.cgColor
ringLayer.strokeColor = fromColor.cgColor
ringLayer.lineWidth = ringWidth
ringLayer.lineCap = .round
ringLayer.lineDashPattern = [1]
return ringLayer
}
func loadingAnimation() -> CAAnimation {
let groupAnimation = CAAnimationGroup()
groupAnimation.animations = [
rotationAnimation(),
strokeColorAnimation(from: fromColor, to: toColor),
lineDashPhaseAnimation()
]
groupAnimation.duration = duration
groupAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
groupAnimation.autoreverses = true
return groupAnimation
}
func strokeColorAnimation(from: UIColor, to: UIColor) -> CABasicAnimation {
let fillColorAnimation = CABasicAnimation(keyPath: "strokeColor")
fillColorAnimation.fromValue = from.cgColor
fillColorAnimation.toValue = to.cgColor
return fillColorAnimation
}
func rotationAnimation() -> CABasicAnimation {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.fromValue = 0
rotationAnimation.toValue = CGFloat.pi * 200 / 180
return rotationAnimation
}
func lineDashPhaseAnimation() -> CABasicAnimation {
let lineDashPhaseAnimation = CABasicAnimation(keyPath: "lineDashPattern")
lineDashPhaseAnimation.fromValue = 1
lineDashPhaseAnimation.toValue = 15
return lineDashPhaseAnimation
}
}
extension String {
/// ref: https://stackoverflow.com/a/55339365
func toImage(size: CGSize, color: UIColor) -> UIImage? {
let nsString = (self as NSString)
let font = UIFont.systemFont(ofSize: 1024)
let stringAttributes = [NSAttributedString.Key.font: font]
let imageSize = nsString.size(withAttributes: stringAttributes)
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
color.set()
UIRectFill(CGRect(origin: CGPoint(), size: imageSize))
nsString.draw(at: CGPoint.zero, withAttributes: stringAttributes)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment