mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
1075 lines
45 KiB
Swift
1075 lines
45 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
|
|
public enum ContainedViewLayoutTransitionCurve {
|
|
case linear
|
|
case easeInOut
|
|
case spring
|
|
case custom(Float, Float, Float, Float)
|
|
|
|
public static var slide: ContainedViewLayoutTransitionCurve {
|
|
return .custom(0.33, 0.52, 0.25, 0.99)
|
|
}
|
|
}
|
|
|
|
public extension ContainedViewLayoutTransitionCurve {
|
|
var timingFunction: String {
|
|
switch self {
|
|
case .linear:
|
|
return CAMediaTimingFunctionName.linear.rawValue
|
|
case .easeInOut:
|
|
return CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
|
case .spring:
|
|
return kCAMediaTimingFunctionSpring
|
|
case .custom:
|
|
return CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
|
}
|
|
}
|
|
|
|
var mediaTimingFunction: CAMediaTimingFunction? {
|
|
switch self {
|
|
case .linear:
|
|
return nil
|
|
case .easeInOut:
|
|
return nil
|
|
case .spring:
|
|
return nil
|
|
case let .custom(p1, p2, p3, p4):
|
|
return CAMediaTimingFunction(controlPoints: p1, p2, p3, p4)
|
|
}
|
|
}
|
|
|
|
#if os(iOS)
|
|
var viewAnimationOptions: UIView.AnimationOptions {
|
|
switch self {
|
|
case .linear:
|
|
return [.curveLinear]
|
|
case .easeInOut:
|
|
return [.curveEaseInOut]
|
|
case .spring:
|
|
return UIView.AnimationOptions(rawValue: 7 << 16)
|
|
case .custom:
|
|
return []
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public enum ContainedViewLayoutTransition {
|
|
case immediate
|
|
case animated(duration: Double, curve: ContainedViewLayoutTransitionCurve)
|
|
|
|
public var isAnimated: Bool {
|
|
if case .immediate = self {
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
public extension ContainedViewLayoutTransition {
|
|
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
|
if frame.origin.x.isNaN {
|
|
return
|
|
}
|
|
if frame.origin.y.isNaN {
|
|
return
|
|
}
|
|
if frame.size.width.isNaN {
|
|
return
|
|
}
|
|
if frame.size.width < 0.0 {
|
|
return
|
|
}
|
|
if frame.size.height.isNaN {
|
|
return
|
|
}
|
|
if frame.size.height < 0.0 {
|
|
return
|
|
}
|
|
if !ASIsCGRectValidForLayout(CGRect(origin: CGPoint(), size: frame.size)) {
|
|
return
|
|
}
|
|
if !ASIsCGPositionValidForLayout(frame.origin) {
|
|
return
|
|
}
|
|
|
|
if node.frame.equalTo(frame) && !force {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
node.frame = frame
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousFrame: CGRect
|
|
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
|
previousFrame = presentation.frame
|
|
} else {
|
|
previousFrame = node.frame
|
|
}
|
|
node.frame = frame
|
|
node.layer.animateFrame(from: previousFrame, to: frame, duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateFrameAsPositionAndBounds(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if node.frame.equalTo(frame) && !force {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
node.position = frame.center
|
|
node.bounds = CGRect(origin: CGPoint(), size: frame.size)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousPosition: CGPoint
|
|
let previousBounds: CGRect
|
|
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
|
previousPosition = presentation.position
|
|
previousBounds = presentation.bounds
|
|
} else {
|
|
previousPosition = node.position
|
|
previousBounds = node.bounds
|
|
}
|
|
node.position = frame.center
|
|
node.bounds = CGRect(origin: CGPoint(), size: frame.size)
|
|
node.layer.animateFrame(from:
|
|
CGRect(origin: CGPoint(x: previousPosition.x - previousBounds.width / 2.0, y: previousPosition.y - previousBounds.height / 2.0), size: previousBounds.size), to: frame, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateFrameAdditive(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if node.frame.equalTo(frame) && !force {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
node.frame = frame
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case .animated:
|
|
let previousFrame = node.frame
|
|
node.frame = frame
|
|
self.animatePositionAdditive(node: node, offset: CGPoint(x: previousFrame.minX - frame.minX, y: previousFrame.minY - frame.minY))
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateFrameAdditiveToCenter(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if node.frame.equalTo(frame) && !force {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
node.position = frame.center
|
|
node.bounds = CGRect(origin: node.bounds.origin, size: frame.size)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case .animated:
|
|
let previousCenter = node.frame.center
|
|
node.position = frame.center
|
|
node.bounds = CGRect(origin: node.bounds.origin, size: frame.size)
|
|
self.animatePositionAdditive(node: node, offset: CGPoint(x: previousCenter.x - frame.midX, y: previousCenter.y - frame.midY))
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if node.bounds.equalTo(bounds) && !force {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
node.bounds = bounds
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousBounds = node.bounds
|
|
node.bounds = bounds
|
|
node.layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateBounds(layer: CALayer, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if layer.bounds.equalTo(bounds) && !force {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
layer.bounds = bounds
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousBounds = layer.bounds
|
|
layer.bounds = bounds
|
|
layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func updatePosition(node: ASDisplayNode, position: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if node.position.equalTo(position) {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
node.position = position
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousPosition: CGPoint
|
|
if beginWithCurrentState {
|
|
previousPosition = node.layer.presentation()?.position ?? node.position
|
|
} else {
|
|
previousPosition = node.position
|
|
}
|
|
node.position = position
|
|
node.layer.animatePosition(from: previousPosition, to: position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func updatePosition(layer: CALayer, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
|
if layer.position.equalTo(position) {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
layer.position = position
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousPosition = layer.position
|
|
layer.position = position
|
|
layer.animatePosition(from: previousPosition, to: position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func animatePosition(node: ASDisplayNode, from position: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
|
switch self {
|
|
case .immediate:
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
node.layer.animatePosition(from: position, to: node.position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func animatePosition(node: ASDisplayNode, to position: CGPoint, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if !additive && node.position.equalTo(position) {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
node.layer.animatePosition(from: additive ? CGPoint() : node.position, to: position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func animateFrame(node: ASDisplayNode, from frame: CGRect, to toFrame: CGRect? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
switch self {
|
|
case .immediate:
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
node.layer.animateFrame(from: frame, to: toFrame ?? node.layer.frame, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func animateBounds(layer: CALayer, from bounds: CGRect, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
|
switch self {
|
|
case .immediate:
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
layer.animateBounds(from: bounds, to: layer.bounds, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func animateOffsetAdditive(node: ASDisplayNode, offset: CGFloat) {
|
|
switch self {
|
|
case .immediate:
|
|
break
|
|
case let .animated(duration, curve):
|
|
node.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction)
|
|
}
|
|
}
|
|
|
|
func animateHorizontalOffsetAdditive(node: ASDisplayNode, offset: CGFloat, completion: (() -> Void)? = nil) {
|
|
switch self {
|
|
case .immediate:
|
|
break
|
|
case let .animated(duration, curve):
|
|
node.layer.animateBoundsOriginXAdditive(from: offset, to: 0.0, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { _ in
|
|
completion?()
|
|
})
|
|
}
|
|
}
|
|
|
|
func animateOffsetAdditive(layer: CALayer, offset: CGFloat, completion: (() -> Void)? = nil) {
|
|
switch self {
|
|
case .immediate:
|
|
completion?()
|
|
case let .animated(duration, curve):
|
|
layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { _ in
|
|
completion?()
|
|
})
|
|
}
|
|
}
|
|
|
|
func animatePositionAdditive(node: ASDisplayNode, offset: CGFloat, removeOnCompletion: Bool = true, completion: @escaping (Bool) -> Void) {
|
|
switch self {
|
|
case .immediate:
|
|
completion(true)
|
|
case let .animated(duration, curve):
|
|
node.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
|
|
}
|
|
}
|
|
|
|
func animatePositionAdditive(layer: CALayer, offset: CGFloat, removeOnCompletion: Bool = true, completion: @escaping (Bool) -> Void) {
|
|
switch self {
|
|
case .immediate:
|
|
completion(true)
|
|
case let .animated(duration, curve):
|
|
layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
|
|
}
|
|
}
|
|
|
|
func animatePositionAdditive(node: ASDisplayNode, offset: CGPoint, removeOnCompletion: Bool = true, completion: (() -> Void)? = nil) {
|
|
switch self {
|
|
case .immediate:
|
|
completion?()
|
|
case let .animated(duration, curve):
|
|
node.layer.animatePosition(from: offset, to: CGPoint(), duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: { _ in
|
|
completion?()
|
|
})
|
|
}
|
|
}
|
|
|
|
func animatePositionAdditive(layer: CALayer, offset: CGPoint, to toOffset: CGPoint = CGPoint(), removeOnCompletion: Bool = true, completion: (() -> Void)? = nil) {
|
|
switch self {
|
|
case .immediate:
|
|
completion?()
|
|
case let .animated(duration, curve):
|
|
layer.animatePosition(from: offset, to: toOffset, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: { _ in
|
|
completion?()
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateFrame(view: UIView, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if view.frame.equalTo(frame) && !force {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
view.frame = frame
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousFrame = view.frame
|
|
view.frame = frame
|
|
view.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)? = nil) {
|
|
if layer.frame.equalTo(frame) {
|
|
completion?(true)
|
|
} else {
|
|
switch self {
|
|
case .immediate:
|
|
layer.frame = frame
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousFrame = layer.frame
|
|
layer.frame = frame
|
|
layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateAlpha(node: ASDisplayNode, alpha: CGFloat, beginWithCurrentState: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if node.alpha.isEqual(to: alpha) && !force {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.alpha = alpha
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousAlpha: CGFloat
|
|
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
|
previousAlpha = CGFloat(presentation.opacity)
|
|
} else {
|
|
previousAlpha = node.alpha
|
|
}
|
|
node.alpha = alpha
|
|
node.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateAlpha(layer: CALayer, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
|
if layer.opacity.isEqual(to: Float(alpha)) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
layer.opacity = Float(alpha)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousAlpha = layer.opacity
|
|
layer.opacity = Float(alpha)
|
|
layer.animateAlpha(from: CGFloat(previousAlpha), to: alpha, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateBackgroundColor(node: ASDisplayNode, color: UIColor, completion: ((Bool) -> Void)? = nil) {
|
|
if let nodeColor = node.backgroundColor, nodeColor.isEqual(color) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.backgroundColor = color
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
if let nodeColor = node.backgroundColor {
|
|
node.backgroundColor = color
|
|
node.layer.animate(from: nodeColor.cgColor, to: color.cgColor, keyPath: "backgroundColor", timingFunction: curve.timingFunction, duration: duration, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
} else {
|
|
node.backgroundColor = color
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateCornerRadius(node: ASDisplayNode, cornerRadius: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
|
if node.cornerRadius.isEqual(to: cornerRadius) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.cornerRadius = cornerRadius
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousCornerRadius = node.cornerRadius
|
|
node.cornerRadius = cornerRadius
|
|
node.layer.animate(from: NSNumber(value: Float(previousCornerRadius)), to: NSNumber(value: Float(cornerRadius)), keyPath: "cornerRadius", timingFunction: curve.timingFunction, duration: duration, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
|
if layer.cornerRadius.isEqual(to: cornerRadius) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
layer.cornerRadius = cornerRadius
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousCornerRadius = layer.cornerRadius
|
|
layer.cornerRadius = cornerRadius
|
|
layer.animate(from: NSNumber(value: Float(previousCornerRadius)), to: NSNumber(value: Float(cornerRadius)), keyPath: "cornerRadius", timingFunction: curve.timingFunction, duration: duration, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func animateTransformScale(node: ASDisplayNode, from fromScale: CGFloat, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
let t = node.layer.transform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
if currentScale.isEqual(to: fromScale) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let calculatedFrom: CGFloat
|
|
let calculatedTo: CGFloat
|
|
if additive {
|
|
calculatedFrom = fromScale - currentScale
|
|
calculatedTo = 0.0
|
|
} else {
|
|
calculatedFrom = fromScale
|
|
calculatedTo = currentScale
|
|
}
|
|
node.layer.animateScale(from: calculatedFrom, to: calculatedTo, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, additive: additive, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func animateTransformScale(view: UIView, from fromScale: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
|
let t = view.layer.transform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
if currentScale.isEqual(to: fromScale) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
view.layer.animateScale(from: fromScale, to: currentScale, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateTransformScale(node: ASDisplayNode, scale: CGFloat, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
let t = node.layer.transform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
if currentScale.isEqual(to: scale) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousScale: CGFloat
|
|
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
|
let t = presentation.transform
|
|
previousScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
} else {
|
|
previousScale = currentScale
|
|
}
|
|
node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
node.layer.animateScale(from: previousScale, to: scale, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateTransformScale(layer: CALayer, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
|
let t = layer.transform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
if currentScale.isEqual(to: scale) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
layer.animateScale(from: currentScale, to: scale, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
|
if !node.isNodeLoaded {
|
|
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
completion?(true)
|
|
return
|
|
}
|
|
let t = node.layer.sublayerTransform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
if currentScale.isEqual(to: scale) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
|
result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateSublayerTransformScaleAdditive(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
|
|
if !node.isNodeLoaded {
|
|
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
completion?(true)
|
|
return
|
|
}
|
|
let t = node.layer.sublayerTransform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
if currentScale.isEqual(to: scale) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let t = node.layer.sublayerTransform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
node.layer.animate(from: -(scale - currentScale) as NSNumber, to: 0.0 as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: true, completion: {
|
|
result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateSublayerTransformScaleAndOffset(node: ASDisplayNode, scale: CGFloat, offset: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
if !node.isNodeLoaded {
|
|
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
|
completion?(true)
|
|
return
|
|
}
|
|
let t = node.layer.sublayerTransform
|
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
let currentOffset = CGPoint(x: t.m41 / currentScale, y: t.m42 / currentScale)
|
|
if abs(currentScale - scale) <= CGFloat.ulpOfOne && abs(currentOffset.x - offset.x) <= CGFloat.ulpOfOne && abs(currentOffset.y - offset.y) <= CGFloat.ulpOfOne {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
let transform = CATransform3DTranslate(CATransform3DMakeScale(scale, scale, 1.0), offset.x, offset.y, 0.0)
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.layer.sublayerTransform = transform
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let initialTransform: CATransform3D
|
|
if beginWithCurrentState, node.isNodeLoaded {
|
|
initialTransform = node.layer.presentation()?.sublayerTransform ?? t
|
|
} else {
|
|
initialTransform = t
|
|
}
|
|
|
|
node.layer.sublayerTransform = transform
|
|
node.layer.animate(from: NSValue(caTransform3D: initialTransform), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
|
result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
|
if !node.isNodeLoaded {
|
|
node.subnodeTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
completion?(true)
|
|
return
|
|
}
|
|
let t = node.layer.sublayerTransform
|
|
let currentScaleX = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
var currentScaleY = sqrt((t.m21 * t.m21) + (t.m22 * t.m22) + (t.m23 * t.m23))
|
|
if t.m22 < 0.0 {
|
|
currentScaleY = -currentScaleY
|
|
}
|
|
if CGPoint(x: currentScaleX, y: currentScaleY) == scale {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.layer.sublayerTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
node.layer.sublayerTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
|
result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateSublayerTransformScale(layer: CALayer, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
|
let t = layer.sublayerTransform
|
|
let currentScaleX = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
var currentScaleY = sqrt((t.m21 * t.m21) + (t.m22 * t.m22) + (t.m23 * t.m23))
|
|
if t.m22 < 0.0 {
|
|
currentScaleY = -currentScaleY
|
|
}
|
|
if CGPoint(x: currentScaleX, y: currentScaleY) == scale {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
layer.sublayerTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
layer.sublayerTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
|
result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateTransformScale(node: ASDisplayNode, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
|
if !node.isNodeLoaded {
|
|
node.subnodeTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
completion?(true)
|
|
return
|
|
}
|
|
let t = node.layer.transform
|
|
let currentScaleX = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
|
var currentScaleY = sqrt((t.m21 * t.m21) + (t.m22 * t.m22) + (t.m23 * t.m23))
|
|
if t.m22 < 0.0 {
|
|
currentScaleY = -currentScaleY
|
|
}
|
|
if CGPoint(x: currentScaleX, y: currentScaleY) == scale {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.layer.transform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
node.layer.transform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
|
|
node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.transform), keyPath: "transform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
|
result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateSublayerTransformOffset(layer: CALayer, offset: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
|
let t = layer.sublayerTransform
|
|
let currentOffset = CGPoint(x: t.m41, y: t.m42)
|
|
if currentOffset == offset {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
layer.sublayerTransform = CATransform3DMakeTranslation(offset.x, offset.y, 0.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
layer.sublayerTransform = CATransform3DMakeTranslation(offset.x, offset.y, 0.0)
|
|
layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
|
result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateTransformRotation(node: ASDisplayNode, angle: CGFloat, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
let t = node.layer.transform
|
|
let currentAngle = atan2(t.m12, t.m11)
|
|
if currentAngle.isEqual(to: angle) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
node.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousAngle: CGFloat
|
|
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
|
let t = presentation.transform
|
|
previousAngle = atan2(t.m12, t.m11)
|
|
} else {
|
|
previousAngle = currentAngle
|
|
}
|
|
node.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
|
|
node.layer.animateRotation(from: previousAngle, to: angle, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateTransformRotation(view: UIView, angle: CGFloat, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
let t = view.layer.transform
|
|
let currentAngle = atan2(t.m12, t.m11)
|
|
if currentAngle.isEqual(to: angle) {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
view.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousAngle: CGFloat
|
|
if beginWithCurrentState, let presentation = view.layer.presentation() {
|
|
let t = presentation.transform
|
|
previousAngle = atan2(t.m12, t.m11)
|
|
} else {
|
|
previousAngle = currentAngle
|
|
}
|
|
view.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
|
|
view.layer.animateRotation(from: previousAngle, to: angle, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func updateTransformRotationAndScale(view: UIView, angle: CGFloat, scale: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
|
let t = view.layer.transform
|
|
let currentAngle = atan2(t.m12, t.m11)
|
|
let currentScale = CGPoint(x: t.m11, y: t.m12)
|
|
if currentAngle.isEqual(to: angle) && currentScale == scale {
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .immediate:
|
|
view.layer.transform = CATransform3DRotate(CATransform3DMakeScale(scale.x, scale.y, 1.0), angle, 0.0, 0.0, 1.0)
|
|
if let completion = completion {
|
|
completion(true)
|
|
}
|
|
case let .animated(duration, curve):
|
|
let previousAngle: CGFloat
|
|
if beginWithCurrentState, let presentation = view.layer.presentation() {
|
|
let t = presentation.transform
|
|
previousAngle = atan2(t.m12, t.m11)
|
|
} else {
|
|
previousAngle = currentAngle
|
|
}
|
|
view.layer.transform = CATransform3DRotate(CATransform3DMakeScale(scale.x, scale.y, 1.0), angle, 0.0, 0.0, 1.0)
|
|
view.layer.animateRotation(from: previousAngle, to: angle, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
|
if let completion = completion {
|
|
completion(result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
#if os(iOS)
|
|
|
|
public extension ContainedViewLayoutTransition {
|
|
func animateView(_ f: @escaping () -> Void) {
|
|
switch self {
|
|
case .immediate:
|
|
f()
|
|
case let .animated(duration, curve):
|
|
UIView.animate(withDuration: duration, delay: 0.0, options: curve.viewAnimationOptions, animations: {
|
|
f()
|
|
}, completion: nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|