Remove local Flow files.

This commit is contained in:
SBiOSoftWhare
2020-09-01 11:31:53 +02:00
parent a7474a3866
commit 15551ec9b4
13 changed files with 0 additions and 994 deletions

View File

@@ -1,151 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
open class Animation: NSObject, CAAnimationDelegate {
/// Key frame animations which animate the properties of `layer`.
fileprivate var keyframeAnimations: [CAKeyframeAnimation]
/// The CALayer whose properties are animated.
open var layer: CALayer
/// Specifies whether or not the animation should autoreverse.
var autoreverses: Bool
/// Determines the number of times the animation will repeat.
///
/// May be fractional. If the repeatCount is 0, it is ignored.
/// Setting this property to greatestFiniteMagnitude will cause the animation to repeat forever.
var repeatCount: Float
weak var delegate: AnimationDelegate?
/// The current time of the animation. i.e. what time is being displayed.
var time: TimeInterval {
return layer.timeOffset
}
/// True if the animation is playing.
var playing: Bool {
return layer.speed != 0.0
}
// MARK: - Initializers
public init(layer: CALayer, keyframeAnimations: [CAKeyframeAnimation], autoreverses: Bool = false, repeatCount: Float = 0) {
self.layer = layer
self.keyframeAnimations = keyframeAnimations
self.autoreverses = autoreverses
self.repeatCount = repeatCount
super.init()
keyframeAnimations.forEach(configure)
reset()
}
private func configure(keyframeAnimation: CAKeyframeAnimation) {
keyframeAnimation.delegate = self
keyframeAnimation.isRemovedOnCompletion = false
keyframeAnimation.fillMode = .both
keyframeAnimation.autoreverses = autoreverses
keyframeAnimation.repeatCount = repeatCount
}
// MARK: - Playing & Resetting Animation
/// Resumes the animation from where it was paused.
open func play() {
let pausedTime = layer.timeOffset
layer.speed = 1.0
layer.timeOffset = 0.0
layer.beginTime = 0.0
let timeSincePause = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
layer.beginTime = timeSincePause
}
/// Pauses the animation.
open func pause() {
let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
offset(to: pausedTime)
}
/// Resets the animation to time 0.
open func reset() {
CATransaction.suppressAnimations {
layer.removeAllAnimations()
layer.beginTime = 0
offset(to: 0)
for keyframeAnimation in keyframeAnimations {
layer.setValue(keyframeAnimation.values?.first, forKeyPath: keyframeAnimation.keyPath!)
}
addAllAnimations()
layer.speed = 0
}
}
/// Adds all the animations to `layer` so they can be played.
private func addAllAnimations() {
DispatchQueue.main.async { [weak self] in
guard let keyframeAnimations = self?.keyframeAnimations, let layer = self?.layer else {
return
}
for keyframeAnimation in keyframeAnimations {
layer.add(keyframeAnimation, forKey: keyframeAnimation.keyPath)
}
}
}
// MARK: - Driving Animation
/// Shows the animation at time `time`.
open func offset(to time: TimeInterval) {
layer.speed = 0.0
layer.timeOffset = time
}
// MARK: - CAAnimationDelegate
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
guard flag else {
return
}
let time = autoreverses ? 0 : (keyframeAnimations.first?.duration ?? 0)
offset(to: time)
if let keyframeAnimation = anim as? CAKeyframeAnimation,
keyframeAnimations.first?.keyPath == keyframeAnimation.keyPath {
delegate?.didStop(animation: self)
}
}
}
public extension Animation {
var reversed: Animation {
let reversedKeyFrameAnimations = keyframeAnimations.map { $0.reversed }
return Animation(layer: layer, keyframeAnimations: reversedKeyFrameAnimations)
}
}
protocol AnimationDelegate: class {
func didStop(animation: Animation)
}

View File

@@ -1,32 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import Foundation
import UIKit
public extension CAKeyframeAnimation {
var reversed: CAKeyframeAnimation {
let reversed = CAKeyframeAnimation(keyPath: keyPath)
reversed.keyTimes = keyTimes?.reversed().map { NSNumber(floatLiteral: 1.0 - $0.doubleValue) }
reversed.values = values?.reversed()
reversed.timingFunctions = timingFunctions?.reversed().map { $0.reversed }
reversed.duration = duration
return reversed
}
}

View File

@@ -1,51 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import Foundation
import UIKit
public extension CAMediaTimingFunction {
static let linear = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
static let easeIn = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
static let easeOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
static let easeInEaseOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
var reversed: CAMediaTimingFunction {
let (c1, c2) = controlPoints
let x1 = Float(1 - c2.x)
let y1 = Float(1 - c2.y)
let x2 = Float(1 - c1.x)
let y2 = Float(1 - c1.y)
return CAMediaTimingFunction(controlPoints: x1, y1, x2, y2)
}
var controlPoints: (CGPoint, CGPoint) {
var c1: [Float] = [0, 0]
var c2: [Float] = [0, 0]
getControlPoint(at: 1, values: &c1)
getControlPoint(at: 2, values: &c2)
let c1x = CGFloat(c1[0])
let c1y = CGFloat(c1[1])
let c2x = CGFloat(c2[0])
let c2y = CGFloat(c2[1])
return (CGPoint(x: c1x, y: c1y), CGPoint(x: c2x, y: c2y))
}
}

View File

@@ -1,29 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
extension CATransaction {
static public func suppressAnimations(actions: () -> Void) {
begin()
setAnimationDuration(0)
actions()
commit()
}
}

View File

@@ -1,310 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import QuartzCore
public func CGPathCreateWithSVGString(_ string: String) -> CGPath? {
let parser = SVGPathStringParser()
return try? parser.parse(string)
}
class SVGPathStringParser {
enum Error: Swift.Error {
case invalidSyntax
case missingValues
}
let path = CGMutablePath()
var currentPoint = CGPoint()
var lastControlPoint = CGPoint()
var command: UnicodeScalar?
var values = [CGFloat]()
func parse(_ string: String) throws -> CGPath {
let tokens = SVGPathStringTokenizer(string: string).tokenize()
for token in tokens {
switch token {
case .command(let c):
command = c
values.removeAll()
case .value(let v):
values.append(v)
}
do {
try addCommand()
} catch Error.missingValues {
// Ignore
}
}
return path
}
fileprivate func addCommand() throws {
guard let command = command else {
return
}
switch command {
case "M":
if values.count < 2 { throw Error.missingValues }
path.move(to: CGPoint(x: values[0], y: values[1]))
currentPoint = CGPoint(x: values[0], y: values[1])
lastControlPoint = currentPoint
values.removeFirst(2)
case "m":
if values.count < 2 { throw Error.missingValues }
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
path.move(to: point)
currentPoint.x += values[0]
currentPoint.y += values[1]
lastControlPoint = currentPoint
values.removeFirst(2)
case "L":
if values.count < 2 { throw Error.missingValues }
let point = CGPoint(x: values[0], y: values[1])
path.addLine(to: point)
currentPoint = CGPoint(x: values[0], y: values[1])
lastControlPoint = currentPoint
values.removeFirst(2)
case "l":
if values.count < 2 { throw Error.missingValues }
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
path.addLine(to: point)
currentPoint.x += values[0]
currentPoint.y += values[1]
lastControlPoint = currentPoint
values.removeFirst(2)
case "H":
if values.count < 1 { throw Error.missingValues }
let point = CGPoint(x: values[0], y: currentPoint.y)
path.addLine(to: point)
currentPoint.x = values[0]
lastControlPoint = currentPoint
values.removeFirst(1)
case "h":
if values.count < 1 { throw Error.missingValues }
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y)
path.addLine(to: point)
currentPoint.x += values[0]
lastControlPoint = currentPoint
values.removeFirst(1)
case "V":
if values.count < 1 { throw Error.missingValues }
let point = CGPoint(x: currentPoint.x, y: values[0])
path.addLine(to: point)
currentPoint.y = values[0]
lastControlPoint = currentPoint
values.removeFirst(1)
case "v":
if values.count < 1 { throw Error.missingValues }
let point = CGPoint(x: currentPoint.x, y: currentPoint.y + values[0])
path.addLine(to: point)
currentPoint.y += values[0]
lastControlPoint = currentPoint
values.removeFirst(1)
case "C":
if values.count < 6 { throw Error.missingValues }
let c1 = CGPoint(x: values[0], y: values[1])
let c2 = CGPoint(x: values[2], y: values[3])
let to = CGPoint(x: values[4], y: values[5])
path.addCurve(to: to, control1: c1, control2: c2)
lastControlPoint = c2
currentPoint = to
values.removeFirst(6)
case "c":
if values.count < 6 { throw Error.missingValues }
let c1 = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
let c2 = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
let to = CGPoint(x: currentPoint.x + values[4], y: currentPoint.y + values[5])
path.addCurve(to: to, control1: c1, control2: c2)
lastControlPoint = c2
currentPoint = to
values.removeFirst(6)
case "S":
if values.count < 4 { throw Error.missingValues }
var c1 = CGPoint()
c1.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
c1.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
let c2 = CGPoint(x: values[0], y: values[1])
let to = CGPoint(x: values[2], y: values[3])
path.addCurve(to: to, control1: c1, control2: c2)
lastControlPoint = c2
currentPoint = to
values.removeFirst(4)
case "s":
if values.count < 4 { throw Error.missingValues }
var c1 = CGPoint()
c1.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
c1.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
let c2 = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
let to = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
path.addCurve(to: to, control1: c1, control2: c2)
lastControlPoint = c2
currentPoint = to
values.removeFirst(4)
case "Q":
if values.count < 4 { throw Error.missingValues }
let control = CGPoint(x: values[0], y: values[1])
let to = CGPoint(x: values[2], y: values[3])
path.addQuadCurve(to: to, control: control)
lastControlPoint = control
currentPoint = to
values.removeFirst(4)
case "q":
if values.count < 4 { throw Error.missingValues }
let control = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
let to = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
path.addQuadCurve(to: to, control: control)
lastControlPoint = control
currentPoint = to
values.removeFirst(4)
case "T":
if values.count < 2 { throw Error.missingValues }
var control = CGPoint()
control.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
control.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
let to = CGPoint(x: values[0], y: values[1])
path.addQuadCurve(to: to, control: control)
lastControlPoint = control
currentPoint = to
values.removeFirst(2)
case "t":
if values.count < 2 { throw Error.missingValues }
var control = CGPoint()
control.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
control.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
let to = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
path.addQuadCurve(to: to, control: control)
lastControlPoint = control
currentPoint = to
values.removeFirst(2)
case "Z", "z":
path.closeSubpath()
self.command = nil
default:
throw Error.invalidSyntax
}
}
}
class SVGPathStringTokenizer {
enum Token {
case command(UnicodeScalar)
case value(CGFloat)
}
let separatorCharacterSet = CharacterSet(charactersIn: " \t\r\n,")
let commandCharacterSet = CharacterSet(charactersIn: "mMLlHhVvzZCcSsQqTt")
let numberStartCharacterSet = CharacterSet(charactersIn: "-+.0123456789")
let numberCharacterSet = CharacterSet(charactersIn: ".0123456789eE")
var string: String
var range: Range<String.UnicodeScalarView.Index>
init(string: String) {
self.string = string
range = string.unicodeScalars.startIndex..<string.unicodeScalars.endIndex
}
func tokenize() -> [Token] {
var array = [Token]()
while let token = nextToken() {
array.append(token)
}
return array
}
func nextToken() -> Token? {
skipSeparators()
guard let c = get() else {
return nil
}
if commandCharacterSet.isMember(c) {
return .command(c)
}
if numberStartCharacterSet.isMember(c) {
var numberString = String(c)
while let c = peek(), numberCharacterSet.isMember(c) {
numberString += String(c)
get()
}
if let value = Double(numberString) {
return .value(CGFloat(value))
}
}
return nil
}
@discardableResult
func get() -> UnicodeScalar? {
guard range.lowerBound != range.upperBound else {
return nil
}
let c = string.unicodeScalars[range.lowerBound]
range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
return c
}
func peek() -> UnicodeScalar? {
guard range.lowerBound != range.upperBound else {
return nil
}
return string.unicodeScalars[range.lowerBound]
}
func skipSeparators() {
while range.lowerBound != range.upperBound && separatorCharacterSet.isMember(string.unicodeScalars[range.lowerBound]) {
range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
}
}
}
extension CharacterSet {
public func isMember(_ c: UnicodeScalar) -> Bool {
return contains(UnicodeScalar(c.value)!)
}
}

View File

@@ -1,45 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
extension NSMutableParagraphStyle {
convenience init(alignment: NSTextAlignment,
firstLineHeadIndent: CGFloat,
headIndent: CGFloat,
tailIndent: CGFloat,
lineHeightMultiple: CGFloat,
maximumLineHeight: CGFloat,
minimumLineHeight: CGFloat,
lineSpacing: CGFloat,
paragraphSpacing: CGFloat,
paragraphSpacingBefore: CGFloat) {
self.init()
self.alignment = alignment
self.firstLineHeadIndent = firstLineHeadIndent
self.headIndent = headIndent
self.tailIndent = tailIndent
self.lineHeightMultiple = lineHeightMultiple
self.maximumLineHeight = maximumLineHeight
self.minimumLineHeight = minimumLineHeight
self.lineSpacing = lineSpacing
self.paragraphSpacing = paragraphSpacing
self.paragraphSpacingBefore = paragraphSpacingBefore
}
}

View File

@@ -1,29 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
extension NSShadow {
convenience init(blurRadius: CGFloat, offset: CGSize, color: UIColor) {
self.init()
shadowBlurRadius = blurRadius
shadowOffset = offset
shadowColor = color
}
}

View File

@@ -1,77 +0,0 @@
// Copyright © 2016-2019 JABT
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
open class ShapeView: UIView {
open var shapeLayer: CAShapeLayer {
return (layer as? CAShapeLayer)!
}
/// A sublayer which can be used to apply a gradient fill to `self`.
open var gradientLayer: CAGradientLayer? {
set {
// Remove old gradient layer
if let gradientLayer = gradientLayer {
gradientLayer.removeFromSuperlayer()
}
// Replace old gradient with new one
if let newGradientLayer = newValue {
layer.addSublayer(newGradientLayer)
}
}
get {
return layer.sublayers?.first(where: { $0 is CAGradientLayer }) as? CAGradientLayer
}
}
public func addGradient(type: CAGradientLayerType, startPoint: CGPoint, endPoint: CGPoint, stops: [(color: CGColor, location: NSNumber)]) {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = shapeLayer.bounds
self.gradientLayer = gradientLayer
let mask = CAShapeLayer()
mask.path = shapeLayer.path
mask.fillColor = UIColor.black.cgColor
mask.strokeColor = nil
gradientLayer.startPoint = startPoint
gradientLayer.endPoint = endPoint
gradientLayer.colors = stops.map { $0.color }
gradientLayer.locations = stops.map { $0.location }
gradientLayer.type = type
gradientLayer.frame = shapeLayer.bounds
gradientLayer.mask = mask
}
open var path: CGPath? {
get {
return shapeLayer.path
}
set {
shapeLayer.path = newValue
}
}
override open class var layerClass: AnyClass {
return CAShapeLayer.self
}
}

View File

@@ -1,31 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import AVFoundation
public final class Sound {
static func playAudio(_ audio: AVAudioPlayer, delay: TimeInterval) {
audio.prepareToPlay()
let time = DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: time) {
audio.play()
}
}
}

View File

@@ -1,30 +0,0 @@
// Copyright © 2016-2019 JABT
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
open class TextView: UILabel {
open var textLayer: CATextLayer {
return (layer as? CATextLayer)!
}
override open class var layerClass: AnyClass {
return CATextLayer.self
}
}

View File

@@ -1,144 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import Foundation
import UIKit
import AVFoundation
open class Timeline {
public var view: UIView
public var duration: TimeInterval
public let animations: [Animation]
public let sounds: [(sound: AVAudioPlayer, delay: TimeInterval)]
/// Specifies whether or not the timeline's animations autoreverse.
public let autoreverses: Bool
/// Determines the number of times the timeline's animations will repeat.
///
/// May be fractional. If the repeatCount is 0, it is ignored.
/// Setting this property to greatestFiniteMagnitude will cause the timeline to repeat forever.
public let repeatCount: Float
public var time: TimeInterval {
return animations.first?.time ?? 0
}
public var playing: Bool {
return animations.first?.playing ?? false
}
public weak var delegate: TimelineDelegate?
// MARK: - Initializers
public convenience init(view: UIView, animationsByLayer: [CALayer: [CAKeyframeAnimation]], sounds: [(sound: AVAudioPlayer, delay: TimeInterval)], duration: TimeInterval, autoreverses: Bool = false, repeatCount: Float = 0) {
let animations = animationsByLayer.map {
Animation(layer: $0.0, keyframeAnimations: $0.1, autoreverses: autoreverses, repeatCount: repeatCount)
}
self.init(view: view, animations: animations, sounds: sounds, duration: duration, autoreverses: autoreverses, repeatCount: repeatCount)
}
init(view: UIView, animations: [Animation], sounds: [(sound: AVAudioPlayer, delay: TimeInterval)], duration: TimeInterval, autoreverses: Bool, repeatCount: Float) {
self.view = view
self.duration = duration
self.sounds = sounds
self.autoreverses = autoreverses
self.repeatCount = repeatCount
self.animations = animations
self.animations.first?.delegate = self
}
// MARK: - Timeline Playback controls
/// Reset to the initial state of the timeline
public func reset() {
for animation in animations {
animation.reset()
}
delegate?.didReset(timeline: self)
}
/// Resume playing the timeline.
public func play() {
playSounds()
for animation in animations {
animation.play()
}
delegate?.didPlay(timeline: self)
}
private func playSounds() {
for (sound, delay) in sounds {
Sound.playAudio(sound, delay: delay)
}
}
/// Pause playing of timeline.
public func pause() {
for animation in animations {
animation.pause()
}
delegate?.didPause(timeline: self)
}
/// Show timeline at time `time`.
public func offset(to time: TimeInterval) {
let time = max(min(time, duration), 0)
for animation in animations {
animation.offset(to: time)
}
delegate?.didOffset(timeline: self, to: time)
}
/// Returns a reverses version of `self`.
var reversed: Timeline {
let reversedAnimations = animations.map { $0.reversed }
return Timeline(view: view, animations: reversedAnimations, sounds: sounds, duration: duration, autoreverses: autoreverses, repeatCount: repeatCount)
}
}
extension Timeline: AnimationDelegate {
func didStop(animation: Animation) {
delegate?.didStop(timeline: self)
}
}
public protocol TimelineDelegate: class {
/// Informs the delegate that the timeline `timeline` was reset.
func didReset(timeline: Timeline)
/// Informs the delegate that the timeline `timeline` did start playing.
func didPlay(timeline: Timeline)
/// Informs the delegate that the timeline `timeline` was paused.
func didPause(timeline: Timeline)
/// Informs the delegate that the timeline `timeline` was offset.
///
/// - Parameters:
/// - timeline: The timeline which was offset.
/// - time: The time to which `timeline` was offset to.
func didOffset(timeline: Timeline, to time: TimeInterval)
/// Informs the delegate that the timeline `timeline` was stopped because it completed its active duration.
func didStop(timeline: Timeline)
}

View File

@@ -1,35 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
extension UIImage {
func resized(to size: CGSize) -> UIImage {
let rect = CGRect(origin: .zero, size: size)
let vertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height)
return UIGraphicsImageRenderer(size: size).image { _ in
let ctx = UIGraphicsGetCurrentContext()
ctx?.saveGState()
ctx?.concatenate(vertical)
draw(in: rect)
ctx?.restoreGState()
UIGraphicsEndImageContext()
}
}
}

View File

@@ -1,30 +0,0 @@
// Copyright © 2016-2019 JABT Labs Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
extension UIView {
func setTransform(scaleX: CGFloat, scaleY: CGFloat, rotationAngle: CGFloat) {
var transform = CGAffineTransform.identity
transform = transform.concatenating(CGAffineTransform(scaleX: scaleX, y: 1.0))
transform = transform.concatenating(CGAffineTransform(scaleX: 1.0, y: scaleY))
transform = transform.concatenating(CGAffineTransform(rotationAngle: rotationAngle))
self.transform = transform
}
}