511 lines
24 KiB
Swift
511 lines
24 KiB
Swift
//
|
|
// ConnectionLogo.swift
|
|
// PrivadoVPN
|
|
//
|
|
// Created by Lizaveta Malinouskaya on 18.11.21.
|
|
// Copyright © 2021 Privado LLC. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
protocol ConnectionLogoInput {
|
|
func update(state: ConnectionViewColorState)
|
|
func didTapPastClickZone()
|
|
}
|
|
|
|
class ConnectionLogo: UIView, ConnectionLogoInput {
|
|
|
|
enum Constants {
|
|
enum Localized {
|
|
static let tap = NSLocalizedString("connection.lable.tap", comment: "Tap here")
|
|
}
|
|
enum Font {
|
|
static let label = UIFont(name: "Roboto-Medium", size: 14 * Constants.Geometry.multiplier)
|
|
}
|
|
enum AnimationKeys {
|
|
static let opacity = "opacity"
|
|
static let tint = "contentsMultiplyColor"
|
|
static let opacity1 = "OpacityAnimation1"
|
|
static let opacity2 = "OpacityAnimation2"
|
|
static let opacity3 = "OpacityAnimation3"
|
|
static let rotation = "rotationAnimation"
|
|
static let color = "ColorAnimation"
|
|
}
|
|
enum AnimationDuration {
|
|
static let animation: CFTimeInterval = 3 * fadeInOut
|
|
static let fadeInOut: CFTimeInterval = 3
|
|
static let connected: CFTimeInterval = 0.5
|
|
static let color: CFTimeInterval = 8
|
|
static let rotation: CFTimeInterval = 10
|
|
static let disconnect: CFTimeInterval = 0.5
|
|
}
|
|
enum Geometry {
|
|
static let lock: CGFloat = 85 * multiplier
|
|
static let background: CGFloat = 233 * multiplier
|
|
static let innerCircle: CGFloat = 117 * multiplier
|
|
static let middleCircle: CGFloat = 145 * multiplier
|
|
static let outerCircle: CGFloat = 173 * multiplier
|
|
static let rectHeight: CGFloat = 26 * multiplier
|
|
static let rectWidth: CGFloat = 85 * multiplier
|
|
static let labelY: CGFloat = 32 * multiplier
|
|
static let multiplier = DeviceInfoProvider.isIPad ? 0.85 : 0.75
|
|
}
|
|
enum Image {
|
|
static let lockDisconnected = UIImage(named: "ipad.connection.lock.disconnected.new")
|
|
static let background = UIImage(named: "ipad.connection.background.ellipse")
|
|
|
|
static let innerCircle = UIImage(named: "ipad.connection.circle.inner")
|
|
static let middleCircle = UIImage(named: "ipad.connection.circle.middle")
|
|
static let outerCircle = UIImage(named: "ipad.connection.circle.out")
|
|
|
|
static let innerLarge = UIImage(named: "ipad.connection.circle.inner.large")
|
|
static let middleLarge = UIImage(named: "ipad.connection.circle.middle.large")
|
|
static let outerLarge = UIImage(named: "ipad.connection.circle.out.large")
|
|
|
|
static let innerMedium = UIImage(named: "ipad.connection.circle.inner.medium")
|
|
static let middleMedium = UIImage(named: "ipad.connection.circle.middle.medium")
|
|
static let outerMedium = UIImage(named: "ipad.connection.circle.out.medium")
|
|
|
|
static let innerSmall = UIImage(named: "ipad.connection.circle.inner.small")
|
|
static let outerSmall = UIImage(named: "ipad.connection.circle.out.small")
|
|
|
|
}
|
|
enum Color {
|
|
static let clickBackground = Colors.AnimationLock.clickBackground.color
|
|
static let clickLock = Colors.AnimationLock.clickLock.color
|
|
static let clickRings = Colors.AnimationLock.clickRings.color
|
|
static let clickText = Colors.AnimationLock.clickText.color
|
|
static let clickWhite = Colors.basicText.color
|
|
static let backgroundBlue = Colors.AnimationLock.blue.color
|
|
static let connected = Colors.AnimationLock.green.color
|
|
static let disconnected = Colors.AnimationLock.red.color
|
|
static let progress = Colors.AnimationLock.yellow.color
|
|
static let white = Colors.AnimationLock.white.color
|
|
}
|
|
}
|
|
private var state: ConnectionViewColorState? {
|
|
willSet {
|
|
if newValue == self.state {
|
|
self.didEnterTwice = true
|
|
} else {
|
|
self.didEnterTwice = false
|
|
}
|
|
}
|
|
}
|
|
private var connectionProgressColor = Constants.Color.progress
|
|
private var didEnterTwice = false
|
|
private var tapLabel: UIView?
|
|
private var lockView: UIImageView?
|
|
private var innerRingView: UIImageView?
|
|
private var middleRingView: UIImageView?
|
|
private var outerRingView: UIImageView?
|
|
private var backgroundView: UIImageView?
|
|
|
|
private var innerRingViews: [UIImageView] = []
|
|
private var middleRingViews: [UIImageView] = []
|
|
private var outerRingViews: [UIImageView] = []
|
|
|
|
private var foregroundImageView: UIImageView?
|
|
private var didReturnFromBackground = false
|
|
private var clickZoneAnimationFinished = true
|
|
|
|
// MARK: - Init
|
|
init() {
|
|
super.init(frame: .zero)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(self.applicationDidBecomeActive), name: Notification.Name(PrivadoConstants.Application.didBecomeActive), object: nil)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(self.applicationDidBecomeActive), name: Notification.Name(PrivadoConstants.Notification.serversViewDidAppear), object: nil)
|
|
self.createUI()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func didMoveToSuperview() {
|
|
super.didMoveToSuperview()
|
|
let a = "adfdf"
|
|
}
|
|
|
|
// MARK: - selectors
|
|
|
|
// Set animations after application have been closed
|
|
@objc func applicationDidBecomeActive() {
|
|
if self.didReturnFromBackground {
|
|
guard let state = self.state else { return }
|
|
self.update(state: state)
|
|
self.setRotationAnimations()
|
|
}
|
|
self.didReturnFromBackground = true
|
|
}
|
|
|
|
@objc func didTapPastClickZone() {
|
|
if self.state == .disconnected {
|
|
if self.clickZoneAnimationFinished {
|
|
UIView.animate(withDuration: Constants.AnimationDuration.disconnect, delay: 0, options: .autoreverse, animations: {
|
|
self.changeRingsColor(to: Constants.Color.clickRings)
|
|
self.tapLabel?.layer.opacity = 0
|
|
self.tapLabel?.layer.opacity = 1
|
|
self.lockView?.tintColor = Constants.Color.clickLock
|
|
self.backgroundView?.tintColor = Constants.Color.clickBackground
|
|
self.clickZoneAnimationFinished = false
|
|
}, completion: { _ in
|
|
self.tapLabel?.layer.opacity = 0
|
|
self.changeRingsColor(to: Constants.Color.white)
|
|
self.lockView?.tintColor = Constants.Color.disconnected
|
|
self.backgroundView?.tintColor = Constants.Color.backgroundBlue
|
|
self.clickZoneAnimationFinished = true
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - ConnectionLogoInput
|
|
|
|
func update(state: ConnectionViewColorState) {
|
|
self.state = state
|
|
|
|
if !didEnterTwice {
|
|
switch state {
|
|
case .connected:
|
|
self.stopProgressAnimation()
|
|
self.setRotationSpeed(isConnecting: false)
|
|
self.addConnectedAnimation()
|
|
case .connecting:
|
|
self.setRotationSpeed(isConnecting: true)
|
|
self.addProgressAnimation()
|
|
self.changeRingsColor(to: Constants.Color.white)
|
|
case .disconnecting:
|
|
self.stopProgressAnimation()
|
|
self.stopConnectedAnimation()
|
|
UIView.animate(withDuration: Constants.AnimationDuration.disconnect, animations: {
|
|
self.changeRingsColor(to: Constants.Color.disconnected)
|
|
self.lockView?.tintColor = Constants.Color.disconnected
|
|
})
|
|
case .disconnected:
|
|
self.stopProgressAnimation()
|
|
UIView.animate(withDuration: Constants.AnimationDuration.disconnect, animations: {
|
|
self.changeRingsColor(to: Constants.Color.white)
|
|
})
|
|
self.lockView?.tintColor = Constants.Color.disconnected
|
|
case .error:
|
|
self.setRotationSpeed(isConnecting: false)
|
|
self.stopProgressAnimation()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - UI
|
|
private func createUI() {
|
|
self.translatesAutoresizingMaskIntoConstraints = false
|
|
self.backgroundView = self.initializeImage(from: self, image: Constants.Image.background, width: Constants.Geometry.background)
|
|
self.backgroundView?.tintColor = Constants.Color.backgroundBlue
|
|
|
|
self.lockView = self.initializeImage(from: self, image: Constants.Image.lockDisconnected, width: Constants.Geometry.lock)
|
|
|
|
self.innerRingView = self.initializeImage(from: self, image: Constants.Image.innerCircle, width: Constants.Geometry.innerCircle)
|
|
|
|
self.innerRingViews = [self.initializeImage(from: self, image: Constants.Image.innerLarge, width: Constants.Geometry.innerCircle),
|
|
self.initializeImage(from: self, image: Constants.Image.innerMedium, width: Constants.Geometry.innerCircle),
|
|
self.initializeImage(from: self, image: Constants.Image.innerSmall, width: Constants.Geometry.innerCircle)]
|
|
|
|
self.middleRingView = self.initializeImage(from: self, image: Constants.Image.middleCircle, width: Constants.Geometry.middleCircle)
|
|
|
|
self.middleRingViews = [self.initializeImage(from: self, image: Constants.Image.middleLarge, width: Constants.Geometry.middleCircle),
|
|
self.initializeImage(from: self, image: Constants.Image.middleMedium, width: Constants.Geometry.middleCircle)]
|
|
|
|
self.outerRingView = self.initializeImage(from: self, image: Constants.Image.outerCircle, width: Constants.Geometry.outerCircle)
|
|
|
|
self.outerRingViews = [self.initializeImage(from: self, image: Constants.Image.outerLarge, width: Constants.Geometry.outerCircle),
|
|
self.initializeImage(from: self, image: Constants.Image.outerMedium, width: Constants.Geometry.outerCircle),
|
|
self.initializeImage(from: self, image: Constants.Image.outerSmall, width: Constants.Geometry.outerCircle)]
|
|
|
|
self.tapLabel = self.initializeTapLabel(from: self)
|
|
self.setRotationAnimations()
|
|
self.setColorViewsOpacity()
|
|
self.changeRingsColor(to: Constants.Color.white)
|
|
}
|
|
|
|
private func initializeImage(from container: UIView, image: UIImage?, width: CGFloat) -> UIImageView {
|
|
let imageView = UIImageView(image: image)
|
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
|
container.addSubview(imageView)
|
|
imageView.image = imageView.image?.withRenderingMode(.alwaysTemplate)
|
|
NSLayoutConstraint.activate([
|
|
imageView.centerXAnchor.constraint(equalTo: container.centerXAnchor),
|
|
imageView.centerYAnchor.constraint(equalTo: container.centerYAnchor),
|
|
imageView.widthAnchor.constraint(equalToConstant: width),
|
|
imageView.heightAnchor.constraint(equalToConstant: width)
|
|
])
|
|
return imageView
|
|
}
|
|
|
|
private func initializeTapLabel(from container: UIView) -> UIView {
|
|
let view = UIView()
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
view.backgroundColor = Constants.Color.clickWhite
|
|
view.layer.cornerRadius = 10
|
|
let label = UILabel()
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.font = Constants.Font.label
|
|
label.text = Constants.Localized.tap
|
|
label.textColor = Constants.Color.backgroundBlue
|
|
view.addSubview(label)
|
|
view.layer.opacity = 0
|
|
self.addSubview(view)
|
|
NSLayoutConstraint.activate([
|
|
view.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: Constants.Geometry.labelY),
|
|
view.centerXAnchor.constraint(equalTo: self.centerXAnchor),
|
|
view.heightAnchor.constraint(equalToConstant: Constants.Geometry.rectHeight),
|
|
view.widthAnchor.constraint(equalToConstant: Constants.Geometry.rectWidth),
|
|
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
|
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
|
])
|
|
return view
|
|
}
|
|
|
|
// MARK: - animations
|
|
private func setRotationSpeed(isConnecting: Bool) {
|
|
let speed: Float = isConnecting ? 2 : 1.1
|
|
self.layer.timeOffset = self.layer.convertTime(CACurrentMediaTime(), from: nil)
|
|
self.layer.beginTime = CACurrentMediaTime()
|
|
self.layer.speed = speed
|
|
}
|
|
|
|
private func setRotationAnimations() {
|
|
self.innerRingView?.rotate(clockwise: false, duration: Constants.AnimationDuration.rotation)
|
|
self.outerRingView?.rotate(clockwise: false, duration: Constants.AnimationDuration.rotation)
|
|
self.middleRingView?.rotate(clockwise: true, duration: Constants.AnimationDuration.rotation)
|
|
for view in self.innerRingViews {
|
|
view.rotate(clockwise: false, duration: Constants.AnimationDuration.rotation)
|
|
}
|
|
for view in self.outerRingViews {
|
|
view.rotate(clockwise: false, duration: Constants.AnimationDuration.rotation)
|
|
}
|
|
for view in self.middleRingViews {
|
|
view.rotate(clockwise: true, duration: Constants.AnimationDuration.rotation)
|
|
}
|
|
}
|
|
|
|
private func addProgressAnimation() {
|
|
self.setColorShiftAnimation(fromColor: Constants.Color.disconnected, toColor: Constants.Color.progress, duration: Constants.AnimationDuration.color)
|
|
self.addInnerCircleOpacityAnimation(isConnecting: true)
|
|
self.addMidleCircleOpacityAnimation(isConnecting: true)
|
|
self.addOuterCircleOpacityAnimation(isConnecting: true)
|
|
}
|
|
|
|
private func addConnectedAnimation() {
|
|
self.setColorShiftAnimation(fromColor: self.connectionProgressColor, toColor: Constants.Color.connected, duration: Constants.AnimationDuration.color / 2)
|
|
self.addInnerCircleOpacityAnimation(isConnecting: false)
|
|
self.addMidleCircleOpacityAnimation(isConnecting: false)
|
|
self.addOuterCircleOpacityAnimation(isConnecting: false)
|
|
}
|
|
|
|
private func setColorShiftAnimation(fromColor: UIColor, toColor: UIColor, duration: CFTimeInterval) {
|
|
self.addColorShiftAnimation(to: self.lockView, fromColor: fromColor.cgColor, toColor: toColor.cgColor, duration: duration)
|
|
self.lockView?.tintColor = toColor
|
|
for view in self.innerRingViews {
|
|
self.addColorShiftAnimation(to: view, fromColor: fromColor.cgColor, toColor: toColor.cgColor, duration: duration)
|
|
view.tintColor = toColor
|
|
}
|
|
for view in self.outerRingViews {
|
|
self.addColorShiftAnimation(to: view, fromColor: fromColor.cgColor, toColor: toColor.cgColor, duration: duration)
|
|
view.tintColor = toColor
|
|
}
|
|
for view in self.middleRingViews {
|
|
self.addColorShiftAnimation(to: view, fromColor: fromColor.cgColor, toColor: toColor.cgColor, duration: duration)
|
|
view.tintColor = toColor
|
|
}
|
|
}
|
|
|
|
private func changeRingsColor(to color: UIColor) {
|
|
self.innerRingView?.tintColor = color
|
|
self.middleRingView?.tintColor = color
|
|
self.outerRingView?.tintColor = color
|
|
}
|
|
|
|
private func addOuterCircleOpacityAnimation(isConnecting: Bool) {
|
|
if isConnecting {
|
|
// large - large - small - all else {
|
|
var animations: [CABasicAnimation] = []
|
|
animations.append(contentsOf: self.getOpacityAnimations(beginTime: 0, duration: Constants.AnimationDuration.fadeInOut))
|
|
animations.append(contentsOf: self.getOpacityAnimations(beginTime: Constants.AnimationDuration.fadeInOut, duration: Constants.AnimationDuration.fadeInOut))
|
|
let group = CAAnimationGroup()
|
|
group.animations = animations
|
|
group.duration = Constants.AnimationDuration.animation
|
|
group.beginTime = CACurrentMediaTime()
|
|
group.repeatCount = Float.infinity
|
|
self.outerRingViews[safe: 0]?.layer.add(group, forKey: Constants.AnimationKeys.opacity1)
|
|
|
|
// small part
|
|
group.beginTime = CACurrentMediaTime() + 2 * Constants.AnimationDuration.fadeInOut
|
|
group.animations = getOpacityAnimations(beginTime: 0, duration: Constants.AnimationDuration.fadeInOut)
|
|
self.outerRingViews[safe: 2]?.layer.add(group, forKey: Constants.AnimationKeys.opacity2)
|
|
} else {
|
|
UIView.animate(withDuration: Constants.AnimationDuration.connected,
|
|
delay: 2 * Constants.AnimationDuration.connected,
|
|
options: .autoreverse,
|
|
animations: {
|
|
for view in self.outerRingViews {
|
|
view.layer.opacity = 0
|
|
view.layer.opacity = 1
|
|
}
|
|
},
|
|
completion: {_ in
|
|
for view in self.outerRingViews {
|
|
view.layer.opacity = 0
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
private func addMidleCircleOpacityAnimation(isConnecting: Bool) {
|
|
if isConnecting {
|
|
// large - medium - medium - all else {
|
|
let group = CAAnimationGroup()
|
|
group.repeatCount = Float.infinity
|
|
group.duration = Constants.AnimationDuration.animation
|
|
group.beginTime = CACurrentMediaTime()
|
|
group.animations = self.getOpacityAnimations(beginTime: 0, duration: Constants.AnimationDuration.fadeInOut)
|
|
self.middleRingViews[safe: 0]?.layer.add(group, forKey: Constants.AnimationKeys.opacity1)
|
|
|
|
var animations: [CABasicAnimation] = []
|
|
animations.append(contentsOf: self.getOpacityAnimations(beginTime: 0, duration: Constants.AnimationDuration.fadeInOut))
|
|
animations.append(contentsOf: self.getOpacityAnimations(beginTime: Constants.AnimationDuration.fadeInOut, duration: Constants.AnimationDuration.fadeInOut))
|
|
group.beginTime = CACurrentMediaTime() + Constants.AnimationDuration.fadeInOut
|
|
group.animations = animations
|
|
self.middleRingViews[safe: 1]?.layer.add(group, forKey: Constants.AnimationKeys.opacity2)
|
|
} else {
|
|
UIView.animate(withDuration: Constants.AnimationDuration.connected,
|
|
delay: Constants.AnimationDuration.connected,
|
|
options: .autoreverse,
|
|
animations: {
|
|
for view in self.middleRingViews {
|
|
view.layer.opacity = 0
|
|
view.layer.opacity = 1
|
|
}
|
|
},
|
|
completion: {_ in
|
|
for view in self.middleRingViews {
|
|
view.layer.opacity = 0
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
private func addInnerCircleOpacityAnimation(isConnecting: Bool) {
|
|
if isConnecting {
|
|
// medium - medium - small
|
|
|
|
// medium part 2 times
|
|
var animations: [CABasicAnimation] = []
|
|
animations.append(contentsOf: self.getOpacityAnimations(beginTime: 0, duration: Constants.AnimationDuration.fadeInOut))
|
|
animations.append(contentsOf: self.getOpacityAnimations(beginTime: Constants.AnimationDuration.fadeInOut, duration: Constants.AnimationDuration.fadeInOut))
|
|
let group = CAAnimationGroup()
|
|
group.animations = animations
|
|
group.duration = Constants.AnimationDuration.animation
|
|
group.beginTime = CACurrentMediaTime()
|
|
group.repeatCount = Float.infinity
|
|
self.innerRingViews[safe: 0]?.layer.add(group, forKey: Constants.AnimationKeys.opacity1)
|
|
|
|
// small part
|
|
group.beginTime = CACurrentMediaTime() + 2 * Constants.AnimationDuration.fadeInOut
|
|
group.animations = getOpacityAnimations(beginTime: 0, duration: Constants.AnimationDuration.fadeInOut)
|
|
self.innerRingViews[safe: 2]?.layer.add(group, forKey: Constants.AnimationKeys.opacity2)
|
|
} else {
|
|
UIView.animate(withDuration: Constants.AnimationDuration.connected,
|
|
delay: 0,
|
|
options: .autoreverse,
|
|
animations: {
|
|
for view in self.innerRingViews {
|
|
view.layer.opacity = 0
|
|
view.layer.opacity = 1
|
|
}
|
|
},
|
|
completion: {_ in
|
|
for view in self.innerRingViews {
|
|
view.layer.opacity = 0
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
private func getOpacityAnimations(beginTime: CFTimeInterval, duration: CFTimeInterval) -> [CABasicAnimation] {
|
|
let fromZeroAnimation = CABasicAnimation(keyPath: Constants.AnimationKeys.opacity)
|
|
fromZeroAnimation.fromValue = 0
|
|
fromZeroAnimation.toValue = 1
|
|
fromZeroAnimation.duration = duration / 2
|
|
fromZeroAnimation.beginTime = beginTime
|
|
|
|
let toZeroAnimation = CABasicAnimation(keyPath: Constants.AnimationKeys.opacity)
|
|
toZeroAnimation.fromValue = 1
|
|
toZeroAnimation.toValue = 0
|
|
toZeroAnimation.beginTime = beginTime + duration / 2
|
|
toZeroAnimation.duration = duration / 2
|
|
|
|
return [fromZeroAnimation, toZeroAnimation]
|
|
}
|
|
|
|
private func addColorShiftAnimation(to view: UIView?, fromColor: CGColor, toColor: CGColor, duration: CFTimeInterval) {
|
|
|
|
let disconnectedToProgressAnimation = CABasicAnimation(keyPath: Constants.AnimationKeys.tint)
|
|
disconnectedToProgressAnimation.fromValue = fromColor
|
|
disconnectedToProgressAnimation.toValue = toColor
|
|
disconnectedToProgressAnimation.duration = duration
|
|
|
|
view?.layer.add(disconnectedToProgressAnimation, forKey: Constants.AnimationKeys.color)
|
|
}
|
|
|
|
private func stopConnectedAnimation() {
|
|
self.lockView?.layer.removeAnimation(forKey: Constants.AnimationKeys.color)
|
|
self.setColorViewsOpacity()
|
|
}
|
|
|
|
private func stopProgressAnimation() {
|
|
|
|
self.lockView?.layer.removeAnimation(forKey: Constants.AnimationKeys.color)
|
|
let removeAnimations = { (view: UIView) in
|
|
view.layer.removeAnimation(forKey: Constants.AnimationKeys.opacity1)
|
|
view.layer.removeAnimation(forKey: Constants.AnimationKeys.opacity2)
|
|
view.layer.removeAnimation(forKey: Constants.AnimationKeys.opacity3)
|
|
}
|
|
for view in self.innerRingViews {
|
|
removeAnimations(view)
|
|
}
|
|
for view in self.middleRingViews {
|
|
removeAnimations(view)
|
|
}
|
|
for view in self.outerRingViews {
|
|
removeAnimations(view)
|
|
}
|
|
}
|
|
|
|
private func setColorViewsOpacity() {
|
|
for view in self.middleRingViews {
|
|
view.layer.opacity = 0
|
|
}
|
|
for view in self.outerRingViews {
|
|
view.layer.opacity = 0
|
|
}
|
|
for view in self.innerRingViews {
|
|
view.layer.opacity = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
extension UIView {
|
|
func rotate(clockwise: Bool, duration: CFTimeInterval) {
|
|
let rotation = CABasicAnimation(keyPath: "transform.rotation.z")
|
|
var value: Double = 0
|
|
if clockwise {
|
|
value = Double.pi * 2
|
|
} else {
|
|
value = -Double.pi * 2
|
|
}
|
|
rotation.toValue = NSNumber(value: value)
|
|
rotation.duration = duration
|
|
rotation.isCumulative = true
|
|
rotation.repeatCount = Float.greatestFiniteMagnitude
|
|
self.layer.add(rotation, forKey: "RotationAnimation")
|
|
}
|
|
}
|