mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
170 lines
6.4 KiB
Swift
170 lines
6.4 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import LegacyComponents
|
|
|
|
private final class PasscodeLockIconNodeParameters: NSObject {
|
|
let unlockedColor: UIColor
|
|
let lockedColor: UIColor
|
|
let progress: CGFloat
|
|
let fromScale: CGFloat
|
|
let keepLockedColor: Bool
|
|
|
|
init(unlockedColor: UIColor, lockedColor: UIColor, progress: CGFloat, fromScale: CGFloat, keepLockedColor: Bool) {
|
|
self.unlockedColor = unlockedColor
|
|
self.lockedColor = lockedColor
|
|
self.progress = progress
|
|
self.fromScale = fromScale
|
|
self.keepLockedColor = keepLockedColor
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
final class PasscodeLockIconNode: ASDisplayNode {
|
|
var unlockedColor: UIColor = .black {
|
|
didSet {
|
|
self.setNeedsDisplay()
|
|
}
|
|
}
|
|
|
|
private var effectiveProgress: CGFloat = 1.0 {
|
|
didSet {
|
|
self.setNeedsDisplay()
|
|
}
|
|
}
|
|
|
|
private var fromScale: CGFloat = 1.0
|
|
|
|
private var keepLockedColor = false
|
|
|
|
override init() {
|
|
super.init()
|
|
|
|
self.isOpaque = false
|
|
self.backgroundColor = .clear
|
|
}
|
|
|
|
func animateIn(fromScale: CGFloat = 1.0) {
|
|
self.fromScale = fromScale
|
|
|
|
self.pop_removeAllAnimations()
|
|
|
|
let animation = POPBasicAnimation()
|
|
animation.property = (POPAnimatableProperty.property(withName: "progress", initializer: { property in
|
|
property?.readBlock = { node, values in
|
|
values?.pointee = (node as! PasscodeLockIconNode).effectiveProgress
|
|
}
|
|
property?.writeBlock = { node, values in
|
|
(node as! PasscodeLockIconNode).effectiveProgress = values!.pointee
|
|
}
|
|
property?.threshold = 0.01
|
|
}) as! POPAnimatableProperty)
|
|
animation.fromValue = 0.0 as NSNumber
|
|
animation.toValue = 1.0 as NSNumber
|
|
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
|
animation.duration = 0.55
|
|
self.pop_add(animation, forKey: "progress")
|
|
}
|
|
|
|
func animateUnlock() {
|
|
self.fromScale = 1.0
|
|
self.keepLockedColor = true
|
|
self.pop_removeAllAnimations()
|
|
|
|
let animation = POPBasicAnimation()
|
|
animation.property = (POPAnimatableProperty.property(withName: "progress", initializer: { property in
|
|
property?.readBlock = { node, values in
|
|
values?.pointee = (node as! PasscodeLockIconNode).effectiveProgress
|
|
}
|
|
property?.writeBlock = { node, values in
|
|
(node as! PasscodeLockIconNode).effectiveProgress = values!.pointee
|
|
}
|
|
property?.threshold = 0.01
|
|
}) as! POPAnimatableProperty)
|
|
animation.fromValue = 1.0 as NSNumber
|
|
animation.toValue = 0.0 as NSNumber
|
|
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
|
animation.duration = 0.75
|
|
self.pop_add(animation, forKey: "progress")
|
|
}
|
|
|
|
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
|
return PasscodeLockIconNodeParameters(unlockedColor: self.unlockedColor, lockedColor: .white, progress: self.effectiveProgress, fromScale: self.fromScale, keepLockedColor: self.keepLockedColor)
|
|
}
|
|
|
|
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
|
let context = UIGraphicsGetCurrentContext()!
|
|
|
|
if !isRasterizing {
|
|
context.setBlendMode(.copy)
|
|
context.setFillColor(UIColor.clear.cgColor)
|
|
context.fill(bounds)
|
|
}
|
|
|
|
guard let parameters = parameters as? PasscodeLockIconNodeParameters else {
|
|
return
|
|
}
|
|
|
|
let progress = parameters.progress
|
|
let fromScale = parameters.fromScale
|
|
let lockSpan: CGFloat = parameters.keepLockedColor ? 0.5 : 0.85
|
|
let lockProgress = min(1.0, progress / lockSpan)
|
|
|
|
context.translateBy(x: bounds.width / 2.0, y: bounds.height / 2.0)
|
|
context.scaleBy(x: fromScale + (1.0 - fromScale) * lockProgress, y: fromScale + (1.0 - fromScale) * lockProgress)
|
|
context.translateBy(x: -bounds.width / 2.0, y: -bounds.height / 2.0)
|
|
|
|
let color = parameters.keepLockedColor ? parameters.lockedColor : parameters.unlockedColor.mixedWith(parameters.lockedColor, alpha: progress)
|
|
|
|
context.setStrokeColor(color.cgColor)
|
|
|
|
let lineWidth: CGFloat = 3.0
|
|
context.setLineWidth(lineWidth)
|
|
|
|
var topRect: CGRect
|
|
var topRadius: CGFloat
|
|
var offset: CGFloat = 0.0
|
|
if lockProgress < 0.5 {
|
|
topRect = CGRect(x: 19.0, y: lineWidth / 2.0 + 1.0, width: 14.0 * (0.5 - lockProgress) / 0.5, height: 22.0)
|
|
topRadius = 6.0 * (0.5 - lockProgress) * 2.0
|
|
} else {
|
|
let width = 14.0 * (lockProgress - 0.5) * 2.0
|
|
topRect = CGRect(x: 19.0 - width, y: lineWidth / 2.0 + 1.0, width: width, height: 22.0)
|
|
topRadius = 6.0 * (lockProgress - 0.5) * 2.0
|
|
}
|
|
if progress > lockSpan {
|
|
let innerProgress = (progress - lockSpan) / (1.0 - lockSpan)
|
|
if !parameters.keepLockedColor {
|
|
if innerProgress < 0.6 {
|
|
offset = 2.0 * min(1.0, innerProgress / 0.6)
|
|
} else {
|
|
offset = 2.0 * min(1.0, max(0.0, (1.0 - innerProgress) / 0.4))
|
|
}
|
|
}
|
|
|
|
topRect.origin.y += 4.0 * min(1.0, max(0.0, innerProgress / 0.6)) + offset
|
|
}
|
|
let topPath = UIBezierPath(roundedRect: topRect, cornerRadius: topRadius)
|
|
context.addPath(topPath.cgPath)
|
|
context.strokePath()
|
|
|
|
var clearRect: CGRect
|
|
if lockProgress < 0.5 {
|
|
clearRect = CGRect(x: topRect.minX + lineWidth, y: topRect.minY + 11.0, width: 14.0, height: 22.0)
|
|
} else {
|
|
clearRect = CGRect(x: topRect.maxX - 14.0 - lineWidth, y: topRect.minY + 11.0, width: 14.0, height: 22.0)
|
|
}
|
|
|
|
context.setBlendMode(.clear)
|
|
context.clear(clearRect)
|
|
context.setBlendMode(.normal)
|
|
|
|
context.setFillColor(color.cgColor)
|
|
|
|
let basePath = UIBezierPath(roundedRect: CGRect(x: 0.0, y: bounds.height - 21.0 + offset, width: 24.0, height: 19.0), cornerRadius: 3.5)
|
|
context.addPath(basePath.cgPath)
|
|
context.fillPath()
|
|
}
|
|
}
|