mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Video Chat Improvements
This commit is contained in:
201
submodules/TelegramCallsUI/Sources/VoiceChatPinNode.swift
Normal file
201
submodules/TelegramCallsUI/Sources/VoiceChatPinNode.swift
Normal file
@@ -0,0 +1,201 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
|
||||
private let iconImage = generateTintedImage(image: UIImage(bundleImageName: "Call/Pin"), color: .white)
|
||||
|
||||
private final class VoiceChatPinNodeDrawingState: NSObject {
|
||||
let color: UIColor
|
||||
let transition: CGFloat
|
||||
let reverse: Bool
|
||||
|
||||
init(color: UIColor, transition: CGFloat, reverse: Bool) {
|
||||
self.color = color
|
||||
self.transition = transition
|
||||
self.reverse = reverse
|
||||
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
final class VoiceChatPinNode: ASDisplayNode {
|
||||
class State: Equatable {
|
||||
let pinned: Bool
|
||||
let color: UIColor
|
||||
|
||||
init(pinned: Bool, color: UIColor) {
|
||||
self.pinned = pinned
|
||||
self.color = color
|
||||
}
|
||||
|
||||
static func ==(lhs: State, rhs: State) -> Bool {
|
||||
if lhs.pinned != rhs.pinned {
|
||||
return false
|
||||
}
|
||||
if lhs.color.argb != rhs.color.argb {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private class TransitionContext {
|
||||
let startTime: Double
|
||||
let duration: Double
|
||||
let previousState: State
|
||||
|
||||
init(startTime: Double, duration: Double, previousState: State) {
|
||||
self.startTime = startTime
|
||||
self.duration = duration
|
||||
self.previousState = previousState
|
||||
}
|
||||
}
|
||||
|
||||
private var animator: ConstantDisplayLinkAnimator?
|
||||
|
||||
private var hasState = false
|
||||
private var state: State = State(pinned: false, color: .black)
|
||||
private var transitionContext: TransitionContext?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
self.isOpaque = false
|
||||
}
|
||||
|
||||
func update(state: State, animated: Bool) {
|
||||
var animated = animated
|
||||
if !self.hasState {
|
||||
self.hasState = true
|
||||
animated = false
|
||||
}
|
||||
|
||||
if self.state != state {
|
||||
let previousState = self.state
|
||||
self.state = state
|
||||
|
||||
if animated {
|
||||
self.transitionContext = TransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousState: previousState)
|
||||
}
|
||||
|
||||
self.updateAnimations()
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAnimations() {
|
||||
var animate = false
|
||||
let timestamp = CACurrentMediaTime()
|
||||
|
||||
if let transitionContext = self.transitionContext {
|
||||
if transitionContext.startTime + transitionContext.duration < timestamp {
|
||||
self.transitionContext = nil
|
||||
} else {
|
||||
animate = true
|
||||
}
|
||||
}
|
||||
|
||||
if animate {
|
||||
let animator: ConstantDisplayLinkAnimator
|
||||
if let current = self.animator {
|
||||
animator = current
|
||||
} else {
|
||||
animator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
||||
self?.updateAnimations()
|
||||
})
|
||||
self.animator = animator
|
||||
}
|
||||
animator.isPaused = false
|
||||
} else {
|
||||
self.animator?.isPaused = true
|
||||
}
|
||||
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
|
||||
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||
var transitionFraction: CGFloat = self.state.pinned ? 1.0 : 0.0
|
||||
var color = self.state.color
|
||||
|
||||
var reverse = false
|
||||
if let transitionContext = self.transitionContext {
|
||||
let timestamp = CACurrentMediaTime()
|
||||
var t = CGFloat((timestamp - transitionContext.startTime) / transitionContext.duration)
|
||||
t = min(1.0, max(0.0, t))
|
||||
|
||||
if transitionContext.previousState.pinned != self.state.pinned {
|
||||
transitionFraction = self.state.pinned ? t : 1.0 - t
|
||||
|
||||
reverse = transitionContext.previousState.pinned
|
||||
}
|
||||
|
||||
if transitionContext.previousState.color.rgb != color.rgb {
|
||||
color = transitionContext.previousState.color.interpolateTo(color, fraction: t)!
|
||||
}
|
||||
}
|
||||
|
||||
return VoiceChatPinNodeDrawingState(color: color, transition: transitionFraction, reverse: reverse)
|
||||
}
|
||||
|
||||
@objc override public 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? VoiceChatPinNodeDrawingState else {
|
||||
return
|
||||
}
|
||||
|
||||
context.setFillColor(parameters.color.cgColor)
|
||||
|
||||
let clearLineWidth: CGFloat = 2.0
|
||||
let lineWidth: CGFloat = 1.0 + UIScreenPixel
|
||||
if let iconImage = iconImage?.cgImage {
|
||||
context.saveGState()
|
||||
context.translateBy(x: bounds.midX, y: bounds.midY)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.translateBy(x: -bounds.midX, y: -bounds.midY)
|
||||
context.draw(iconImage, in: CGRect(origin: CGPoint(), size: CGSize(width: 48.0, height: 48.0)))
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
if parameters.transition > 0.0 {
|
||||
let startPoint: CGPoint
|
||||
let endPoint: CGPoint
|
||||
|
||||
let origin = CGPoint(x: 14.0, y: 16.0 - UIScreenPixel)
|
||||
let length: CGFloat = 17.0
|
||||
|
||||
if parameters.reverse {
|
||||
startPoint = CGPoint(x: origin.x + length * (1.0 - parameters.transition), y: origin.y + length * (1.0 - parameters.transition)).offsetBy(dx: UIScreenPixel, dy: -UIScreenPixel)
|
||||
endPoint = CGPoint(x: origin.x + length, y: origin.y + length).offsetBy(dx: UIScreenPixel, dy: -UIScreenPixel)
|
||||
} else {
|
||||
startPoint = origin.offsetBy(dx: UIScreenPixel, dy: -UIScreenPixel)
|
||||
endPoint = CGPoint(x: origin.x + length * parameters.transition, y: origin.y + length * parameters.transition).offsetBy(dx: UIScreenPixel, dy: -UIScreenPixel)
|
||||
}
|
||||
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.setLineWidth(clearLineWidth)
|
||||
|
||||
context.move(to: startPoint.offsetBy(dx: 0.0, dy: 1.0 + UIScreenPixel))
|
||||
context.addLine(to: endPoint.offsetBy(dx: 0.0, dy: 1.0 + UIScreenPixel))
|
||||
context.strokePath()
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setStrokeColor(parameters.color.cgColor)
|
||||
context.setLineWidth(lineWidth)
|
||||
context.setLineCap(.round)
|
||||
context.setLineJoin(.round)
|
||||
|
||||
context.move(to: startPoint)
|
||||
context.addLine(to: endPoint)
|
||||
context.strokePath()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user