Skip to content

Instantly share code, notes, and snippets.

@xmollv
Created March 28, 2020 12:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xmollv/8b0f725486c293c3b50161f60d81fc70 to your computer and use it in GitHub Desktop.
Save xmollv/8b0f725486c293c3b50161f60d81fc70 to your computer and use it in GitHub Desktop.
Playground for an animated checkmark/cross view
import UIKit
import PlaygroundSupport
class AnimatedResultView: UIView {
enum Result {
case success
case failure
}
//MARK: Public properties
var lineColor: UIColor = .white {
didSet {
self.shapeLayer.strokeColor = self.lineColor.cgColor
}
}
//MARK: Private properties
private let shapeLayer = CAShapeLayer()
private let tickViewSize: CGFloat
//MARK: Lifecycle
public init(tickViewSize: CGFloat = 80.0) {
self.tickViewSize = tickViewSize
super.init(frame: CGRect(x: 0, y: 0, width: tickViewSize, height: tickViewSize))
self.shapeLayer.lineWidth = 0.10*tickViewSize
self.shapeLayer.lineCap = .round
self.shapeLayer.lineJoin = .round
self.shapeLayer.frame = self.layer.bounds
self.shapeLayer.strokeColor = self.lineColor.cgColor
self.shapeLayer.backgroundColor = UIColor.clear.cgColor
self.shapeLayer.fillColor = nil
self.shapeLayer.strokeEnd = 0.0
self.layer.addSublayer(self.shapeLayer)
self.layer.cornerRadius = tickViewSize / 2.0
self.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.shapeLayer.frame = self.layer.frame
}
override var intrinsicContentSize: CGSize {
return CGSize(width: self.tickViewSize, height: self.tickViewSize)
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
self.intrinsicContentSize
}
//MARK: Public methods
func animate(to result: AnimatedResultView.Result, completion: (() -> Void)? = nil) {
let path = UIBezierPath()
path.lineCapStyle = .round
switch result {
case .success:
path.move(to: CGPoint(x: 0.27*tickViewSize, y: 0.52*tickViewSize))
path.addLine(to: CGPoint(x: 0.42*tickViewSize, y: 0.68*tickViewSize))
path.addLine(to: CGPoint(x: 0.68*tickViewSize, y: 0.32*tickViewSize))
case .failure:
path.move(to: CGPoint(x: 0.25*tickViewSize, y: 0.25*tickViewSize))
path.addLine(to: CGPoint(x: 0.75*tickViewSize, y: 0.75*tickViewSize))
path.move(to: CGPoint(x: 0.75*tickViewSize, y: 0.25*tickViewSize))
path.addLine(to: CGPoint(x: 0.25*tickViewSize, y: 0.75*tickViewSize))
}
self.shapeLayer.path = path.cgPath
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.timingFunction = CAMediaTimingFunction(name: .default)
animation.fillMode = .both
animation.fromValue = 0.0
animation.toValue = 1.0
animation.duration = 0.3
self.shapeLayer.strokeEnd = 1.0
self.shapeLayer.add(animation, forKey: "strokeEnd")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
completion?()
}
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let checkMark = AnimatedResultView()
checkMark.lineColor = .black
checkMark.backgroundColor = .secondarySystemBackground
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
checkMark.animate(to: .success) {
checkMark.animate(to: .failure)
}
}
view.addSubview(checkMark)
self.view = view
}
}
PlaygroundPage.current.liveView = MyViewController()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment