Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Sega-Zero
Created February 16, 2020 10:36
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 Sega-Zero/9a6b784dd2867e6e3da8c272b2924cdc to your computer and use it in GitHub Desktop.
Save Sega-Zero/9a6b784dd2867e6e3da8c272b2924cdc to your computer and use it in GitHub Desktop.
View with waiting animation that seems like a train on a railroad
class TrainAnimationView: UIView {
@IBInspectable var railwayBlockSize: CGSize = CGSize(width: 20.0, height: 10.0) { didSet { self.setupSublayers() } }
@IBInspectable var railwayBlockOffset: CGFloat = 5 { didSet { self.setupSublayers() } }
@IBInspectable var trainSize: CGSize = CGSize(width: 45.0, height: 10.0) { didSet { self.setupSublayers() } }
@IBInspectable var trainStartColor: UIColor = UIColor(219, 69, 58) { didSet { self.setupSublayers() } }
@IBInspectable var trainEndColor: UIColor = UIColor(219, 58, 106) { didSet { self.setupSublayers() } }
@IBInspectable var railwayBlockColor: UIColor = UIColor.gray.withAlphaComponent(0.5) { didSet { self.setupSublayers() } }
@IBInspectable var isAnimating: Bool = false { didSet { self.setupSublayers() } }
// MARK: private methods
private func makeRoundRectLayer(of size: CGSize, color: UIColor? = nil) -> CALayer {
let layer = CALayer()
if let color = color {
layer.backgroundColor = color.cgColor
}
layer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
layer.cornerRadius = 4
return layer
}
private func makeGradientLayer(of size: CGSize) -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [self.trainStartColor.cgColor, self.trainEndColor.cgColor]
gradientLayer.locations = [0, 1]
gradientLayer.startPoint = CGPoint(x: 0.25, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.frame = CGRect(origin: .zero, size: size)
return gradientLayer
}
private func setupSublayers() {
self.layer.sublayers?.removeAll()
guard self.isAnimating else { return }
let linesCount = Int(self.layer.bounds.width / (self.railwayBlockSize.width + self.railwayBlockOffset))
for index in 0..<linesCount {
let railwayBlockLayer = self.makeRoundRectLayer(of: self.railwayBlockSize, color: self.railwayBlockColor)
railwayBlockLayer.frame = CGRect(
x: CGFloat(index) * (self.railwayBlockSize.width + self.railwayBlockOffset),
y: self.layer.bounds.height / 2 - self.railwayBlockSize.height / 2,
width: self.railwayBlockSize.width,
height: self.railwayBlockSize.height
)
self.layer.addSublayer(railwayBlockLayer)
}
let trainLayer = self.makeRoundRectLayer(of: self.trainSize)
trainLayer.frame = CGRect(
x: self.railwayBlockSize.width - self.trainSize.width,
y: self.layer.bounds.height / 2 - self.trainSize.height / 2,
width: self.trainSize.width,
height: self.trainSize.height
)
trainLayer.masksToBounds = true
let gradientLayer = self.makeGradientLayer(of: self.trainSize)
trainLayer.addSublayer(gradientLayer)
gradientLayer.frame = CGRect(origin: .zero, size: self.trainSize)
let translateAnimation = CAKeyframeAnimation(keyPath: "transform.translation.x")
let values = stride(from: self.railwayBlockSize.width - self.trainSize.width, to: self.layer.bounds.width + self.trainSize.width, by: self.railwayBlockSize.width + self.railwayBlockOffset).map { $0 }
translateAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
translateAnimation.values = values
translateAnimation.duration = 1
translateAnimation.repeatCount = HUGE
translateAnimation.isRemovedOnCompletion = false
translateAnimation.isAdditive = true
translateAnimation.calculationMode = .discrete
trainLayer.add(translateAnimation, forKey: "animation")
self.layer.addSublayer(trainLayer)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment