Skip to content

Instantly share code, notes, and snippets.

@aksswami
Created November 7, 2017 10:48
Show Gist options
  • Save aksswami/a1774328af59260c8900100fa27cc9ac to your computer and use it in GitHub Desktop.
Save aksswami/a1774328af59260c8900100fa27cc9ac to your computer and use it in GitHub Desktop.
Animating the lock using core graphics
//
// ViewController.swift
// LockAnimation
//
// Created by Amit Kumar Swami on 07/11/17.
// Copyright © 2017 aks. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
let color = UIColor.init(red: 255/255.0, green: 91/255.0, blue: 84/255.0, alpha: 1)
let button = UIButton(frame: CGRect(x: 100, y: 600, width: 100, height: 50))
var isLocked = true
var circleLayer: CAShapeLayer?
var handleLayer: CAShapeLayer?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.init(red: 255/255.0, green: 91/255.0, blue: 84/255.0, alpha: 1)
view.addSubview(button)
button.addTarget(self, action: #selector(animate), for: .touchUpInside)
button.backgroundColor = .red
createLock()
}
func createLock() {
let outerRectShapeLayer = CAShapeLayer()
let outerRectPath = UIBezierPath.init(roundedRect: CGRect.init(x: 100, y: 200, width: 300, height: 250), cornerRadius: 30)
outerRectShapeLayer.path = outerRectPath.cgPath
outerRectShapeLayer.strokeColor = UIColor.white.cgColor
outerRectShapeLayer.fillColor = color.cgColor
outerRectShapeLayer.lineWidth = 4.0
let innerRectShapeLayer = CAShapeLayer()
let innerRectPath = UIBezierPath.init(roundedRect: CGRect.init(x: 220, y: 315, width: 40, height: 20), cornerRadius: 10)
innerRectShapeLayer.path = innerRectPath.cgPath
innerRectShapeLayer.strokeColor = UIColor.white.cgColor
innerRectShapeLayer.fillColor = color.cgColor
innerRectShapeLayer.lineWidth = 4.0
let innerCircleShapeLayer = CAShapeLayer()
let innerCirclePath = UIBezierPath.init(arcCenter: CGPoint.init(x: 240, y: 325), radius: 12, startAngle: 0, endAngle: 2*CGFloat.pi, clockwise: true)
innerCircleShapeLayer.path = innerCirclePath.cgPath
innerCircleShapeLayer.strokeColor = UIColor.white.cgColor
innerCircleShapeLayer.fillColor = color.cgColor
innerCircleShapeLayer.lineWidth = 4.0
innerCircleShapeLayer.bounds = innerCirclePath.bounds
innerCircleShapeLayer.position = CGPoint(x: 230, y: 325)
let handlePath = UIBezierPath.init()
handlePath.addArc(withCenter: CGPoint.init(x: 250, y: 200), radius: 80, startAngle: CGFloat.pi, endAngle: 2 * CGFloat.pi, clockwise: true)
handlePath.addLine(to: CGPoint(x: 340, y: 200))
handlePath.addArc(withCenter: CGPoint(x: 250, y: 200), radius: 110, startAngle: 0, endAngle: CGFloat.pi, clockwise: false)
handlePath.close()
let rotationPoint = CGPoint(x: 330, y: 200) // The point we are rotating around
let handleShapeLayer = CAShapeLayer()
handleShapeLayer.path = handlePath.cgPath
handleShapeLayer.strokeColor = UIColor.white.cgColor
handleShapeLayer.fillColor = color.cgColor
handleShapeLayer.lineWidth = 4.0
handleShapeLayer.bounds = handlePath.bounds
let minX = handleShapeLayer.bounds.minX
let minY = handleShapeLayer.bounds.minY
let width = handleShapeLayer.bounds.width
let height = handleShapeLayer.bounds.height
let anchorPoint = CGPoint(x: (rotationPoint.x - minX) / width, y: (rotationPoint.y-minY)/height)
handleShapeLayer.anchorPoint = anchorPoint
handleShapeLayer.position = rotationPoint
handleLayer = handleShapeLayer
circleLayer = innerCircleShapeLayer
view.layer.addSublayer(handleShapeLayer)
view.layer.addSublayer(outerRectShapeLayer)
view.layer.addSublayer(innerRectShapeLayer)
view.layer.addSublayer(innerCircleShapeLayer)
}
@objc func animate() {
if isLocked {
isLocked = !isLocked
let translationAnimation = CAKeyframeAnimation(keyPath: "transform.translation.x")
translationAnimation.values = [0.0, 10.0, 19.0, 20.0]
translationAnimation.keyTimes = [0, 0.4, 0.9, 1.0]
translationAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale.x")
scaleAnimation.values = [1, 1.1, 1.2, 1, 0.95, 0.9, 1.1, 1.15, 1.0]
scaleAnimation.keyTimes = [0, 0.1, 0.4, 0.7, 0.94, 0.95, 0.96, 0.98, 1.0]
// scaleAnimation.isAdditive = true
scaleAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
let fadeAnimation = CAKeyframeAnimation(keyPath: "fillColor")
fadeAnimation.values = [color.cgColor, color.cgColor, UIColor.white.cgColor]
fadeAnimation.keyTimes = [0, 0.9, 1.0]
fadeAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)]
let groupAnimation = CAAnimationGroup()
groupAnimation.animations = [translationAnimation, scaleAnimation, fadeAnimation]
groupAnimation.duration = 0.5
groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
groupAnimation.isRemovedOnCompletion = false
groupAnimation.fillMode = kCAFillModeForwards
// let animation = CABasicAnimation(keyPath: "fillColor")
// animation.toValue = UIColor.white.cgColor
//
//
// let springAnimation = CASpringAnimation(keyPath: "position")
// springAnimation.byValue = [40.0, 0]
// springAnimation.damping = 5
// springAnimation.mass = 0.5
//
// let strechAnimation = CASpringAnimation(keyPath: "transform.scale.x")
// strechAnimation.byValue = 0.1
// springAnimation.damping = 5
// springAnimation.mass = 0.5
// strechAnimation.toValue = 1.5
//
//
// let animationGroup = CAAnimationGroup()
// animationGroup.animations = [animation, springAnimation, strechAnimation]
// animationGroup.duration = 1
// // animationGroup.autoreverses = true
// animationGroup.fillMode = kCAFillModeForwards
// animationGroup.isRemovedOnCompletion = false
circleLayer?.add(groupAnimation, forKey: "circleMoveAnimation")
let springHandleAnimation = CASpringAnimation(keyPath: "transform.rotation")
springHandleAnimation.fromValue = 0
springHandleAnimation.toValue = CGFloat.pi * 0.25
springHandleAnimation.duration = 1
springHandleAnimation.damping = 5
springHandleAnimation.mass = 0.5
springHandleAnimation.fillMode = kCAFillModeForwards
springHandleAnimation.isRemovedOnCompletion = false
// springHandleAnimation.autoreverses = true
// springHandleAnimation.repeatCount = Float.infinity
handleLayer?.add(springHandleAnimation, forKey: "handleRotationAnimation")
} else {
isLocked = !isLocked
let springHandleAnimation = CABasicAnimation(keyPath: "transform.rotation")
springHandleAnimation.fromValue = CGFloat.pi * 0.25
springHandleAnimation.toValue = 0
springHandleAnimation.duration = 0.5
springHandleAnimation.fillMode = kCAFillModeForwards
springHandleAnimation.isRemovedOnCompletion = false
handleLayer?.add(springHandleAnimation, forKey: "handleRotationAnimation")
let translationAnimation = CAKeyframeAnimation(keyPath: "transform.translation.x")
translationAnimation.values = [20.0, 10.0, 5.0, 1.0, 0]
translationAnimation.keyTimes = [0, 0.4, 0.9, 0.92, 1.0]
translationAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale.x")
scaleAnimation.values = [1, 1.1, 1.2, 1, 0.95, 0.9, 1.1, 1.15, 1.0]
scaleAnimation.keyTimes = [0, 0.1, 0.4, 0.7, 0.94, 0.95, 0.96, 0.98, 1.0]
// scaleAnimation.isAdditive = true
scaleAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
let fadeAnimation = CAKeyframeAnimation(keyPath: "fillColor")
fadeAnimation.values = [UIColor.white.cgColor, UIColor.white.cgColor, color.cgColor]
fadeAnimation.keyTimes = [0, 0.9, 1.0]
fadeAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)]
let groupAnimation = CAAnimationGroup()
groupAnimation.animations = [translationAnimation, scaleAnimation, fadeAnimation]
groupAnimation.duration = 0.5
groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
groupAnimation.isRemovedOnCompletion = false
groupAnimation.fillMode = kCAFillModeForwards
circleLayer?.add(groupAnimation, forKey: "circleMoveAnimation")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment