mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Various Improvements
This commit is contained in:
@@ -6,6 +6,7 @@ import SwiftSignalKit
|
||||
import RLottieBinding
|
||||
import GZip
|
||||
import AppBundle
|
||||
import ManagedAnimationNode
|
||||
|
||||
public enum SemanticStatusNodeState: Equatable {
|
||||
public struct ProgressAppearance: Equatable {
|
||||
@@ -41,6 +42,7 @@ private protocol SemanticStatusNodeStateDrawingState: NSObjectProtocol {
|
||||
|
||||
private protocol SemanticStatusNodeStateContext: class {
|
||||
var isAnimating: Bool { get }
|
||||
var requestUpdate: () -> Void { get set }
|
||||
|
||||
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeStateDrawingState
|
||||
}
|
||||
@@ -90,10 +92,12 @@ private final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContex
|
||||
final class DrawingState: NSObject, SemanticStatusNodeStateDrawingState {
|
||||
let transitionFraction: CGFloat
|
||||
let icon: SemanticStatusNodeIcon
|
||||
let iconImage: UIImage?
|
||||
|
||||
init(transitionFraction: CGFloat, icon: SemanticStatusNodeIcon) {
|
||||
init(transitionFraction: CGFloat, icon: SemanticStatusNodeIcon, iconImage: UIImage?) {
|
||||
self.transitionFraction = transitionFraction
|
||||
self.icon = icon
|
||||
self.iconImage = iconImage
|
||||
|
||||
super.init()
|
||||
}
|
||||
@@ -119,38 +123,65 @@ private final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContex
|
||||
break
|
||||
case .play:
|
||||
let diameter = size.width
|
||||
|
||||
let factor = diameter / 50.0
|
||||
|
||||
let size = CGSize(width: 15.0, height: 18.0)
|
||||
context.translateBy(x: (diameter - size.width) / 2.0 + 1.5, y: (diameter - size.height) / 2.0)
|
||||
|
||||
|
||||
let size: CGSize
|
||||
var offset: CGFloat = 0.0
|
||||
if let iconImage = self.iconImage {
|
||||
size = iconImage.size
|
||||
} else {
|
||||
offset = 1.5
|
||||
size = CGSize(width: 15.0, height: 18.0)
|
||||
}
|
||||
context.translateBy(x: (diameter - size.width) / 2.0 + offset, y: (diameter - size.height) / 2.0)
|
||||
if (diameter < 40.0) {
|
||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||
context.scaleBy(x: factor, y: factor)
|
||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||
}
|
||||
let _ = try? drawSvgPath(context, path: "M1.71891969,0.209353049 C0.769586558,-0.350676705 0,0.0908839327 0,1.18800046 L0,16.8564753 C0,17.9569971 0.750549162,18.357187 1.67393713,17.7519379 L14.1073836,9.60224049 C15.0318735,8.99626906 15.0094718,8.04970371 14.062401,7.49100858 L1.71891969,0.209353049 ")
|
||||
context.fillPath()
|
||||
if let iconImage = self.iconImage {
|
||||
context.saveGState()
|
||||
let iconRect = CGRect(origin: CGPoint(), size: iconImage.size)
|
||||
context.clip(to: iconRect, mask: iconImage.cgImage!)
|
||||
context.fill(iconRect)
|
||||
context.restoreGState()
|
||||
} else {
|
||||
let _ = try? drawSvgPath(context, path: "M1.71891969,0.209353049 C0.769586558,-0.350676705 0,0.0908839327 0,1.18800046 L0,16.8564753 C0,17.9569971 0.750549162,18.357187 1.67393713,17.7519379 L14.1073836,9.60224049 C15.0318735,8.99626906 15.0094718,8.04970371 14.062401,7.49100858 L1.71891969,0.209353049 ")
|
||||
context.fillPath()
|
||||
}
|
||||
if (diameter < 40.0) {
|
||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||
context.scaleBy(x: 1.0 / 0.8, y: 1.0 / 0.8)
|
||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||
}
|
||||
context.translateBy(x: -(diameter - size.width) / 2.0 - 1.5, y: -(diameter - size.height) / 2.0)
|
||||
context.translateBy(x: -(diameter - size.width) / 2.0 - offset, y: -(diameter - size.height) / 2.0)
|
||||
case .pause:
|
||||
let diameter = size.width
|
||||
|
||||
let factor = diameter / 50.0
|
||||
|
||||
let size = CGSize(width: 15.0, height: 16.0)
|
||||
let size: CGSize
|
||||
if let iconImage = self.iconImage {
|
||||
size = iconImage.size
|
||||
} else {
|
||||
size = CGSize(width: 15.0, height: 16.0)
|
||||
}
|
||||
context.translateBy(x: (diameter - size.width) / 2.0, y: (diameter - size.height) / 2.0)
|
||||
if (diameter < 40.0) {
|
||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||
context.scaleBy(x: factor, y: factor)
|
||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||
}
|
||||
let _ = try? drawSvgPath(context, path: "M0,1.00087166 C0,0.448105505 0.443716645,0 0.999807492,0 L4.00019251,0 C4.55237094,0 5,0.444630861 5,1.00087166 L5,14.9991283 C5,15.5518945 4.55628335,16 4.00019251,16 L0.999807492,16 C0.447629061,16 0,15.5553691 0,14.9991283 L0,1.00087166 Z M10,1.00087166 C10,0.448105505 10.4437166,0 10.9998075,0 L14.0001925,0 C14.5523709,0 15,0.444630861 15,1.00087166 L15,14.9991283 C15,15.5518945 14.5562834,16 14.0001925,16 L10.9998075,16 C10.4476291,16 10,15.5553691 10,14.9991283 L10,1.00087166 ")
|
||||
context.fillPath()
|
||||
if let iconImage = self.iconImage {
|
||||
context.saveGState()
|
||||
let iconRect = CGRect(origin: CGPoint(), size: iconImage.size)
|
||||
context.clip(to: iconRect, mask: iconImage.cgImage!)
|
||||
context.fill(iconRect)
|
||||
context.restoreGState()
|
||||
} else {
|
||||
let _ = try? drawSvgPath(context, path: "M0,1.00087166 C0,0.448105505 0.443716645,0 0.999807492,0 L4.00019251,0 C4.55237094,0 5,0.444630861 5,1.00087166 L5,14.9991283 C5,15.5518945 4.55628335,16 4.00019251,16 L0.999807492,16 C0.447629061,16 0,15.5553691 0,14.9991283 L0,1.00087166 Z M10,1.00087166 C10,0.448105505 10.4437166,0 10.9998075,0 L14.0001925,0 C14.5523709,0 15,0.444630861 15,1.00087166 L15,14.9991283 C15,15.5518945 14.5562834,16 14.0001925,16 L10.9998075,16 C10.4476291,16 10,15.5553691 10,14.9991283 L10,1.00087166 ")
|
||||
context.fillPath()
|
||||
}
|
||||
if (diameter < 40.0) {
|
||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||
context.scaleBy(x: 1.0 / 0.8, y: 1.0 / 0.8)
|
||||
@@ -159,7 +190,6 @@ private final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContex
|
||||
context.translateBy(x: -(diameter - size.width) / 2.0, y: -(diameter - size.height) / 2.0)
|
||||
case let .custom(image):
|
||||
let diameter = size.width
|
||||
|
||||
let imageRect = CGRect(origin: CGPoint(x: floor((diameter - image.size.width) / 2.0), y: floor((diameter - image.size.height) / 2.0)), size: image.size)
|
||||
|
||||
context.saveGState()
|
||||
@@ -210,18 +240,36 @@ private final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContex
|
||||
}
|
||||
}
|
||||
|
||||
let icon: SemanticStatusNodeIcon
|
||||
var icon: SemanticStatusNodeIcon {
|
||||
didSet {
|
||||
self.animationNode?.enqueueState(self.icon == .play ? .play : .pause, animated: self.iconImage != nil)
|
||||
}
|
||||
}
|
||||
|
||||
var animationNode: PlayPauseIconNode?
|
||||
var iconImage: UIImage?
|
||||
|
||||
init(icon: SemanticStatusNodeIcon) {
|
||||
self.icon = icon
|
||||
|
||||
if [.play, .pause].contains(icon) {
|
||||
self.animationNode = PlayPauseIconNode()
|
||||
self.animationNode?.imageUpdated = { [weak self] image in
|
||||
self?.iconImage = image
|
||||
self?.requestUpdate()
|
||||
}
|
||||
self.iconImage = self.animationNode?.image
|
||||
}
|
||||
}
|
||||
|
||||
var isAnimating: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var requestUpdate: () -> Void = {}
|
||||
|
||||
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeStateDrawingState {
|
||||
return DrawingState(transitionFraction: transitionFraction, icon: self.icon)
|
||||
return DrawingState(transitionFraction: transitionFraction, icon: self.icon, iconImage: self.iconImage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +424,8 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
|
||||
return true
|
||||
}
|
||||
|
||||
var requestUpdate: () -> Void = {}
|
||||
|
||||
init(value: CGFloat?, displayCancel: Bool, appearance: SemanticStatusNodeState.ProgressAppearance?) {
|
||||
self.value = value
|
||||
self.displayCancel = displayCancel
|
||||
@@ -402,6 +452,10 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
|
||||
return DrawingState(transitionFraction: transitionFraction, value: resolvedValue, displayCancel: self.displayCancel, appearance: self.appearance, timestamp: timestamp)
|
||||
}
|
||||
|
||||
func maskView() -> UIView? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateValue(value: CGFloat?) {
|
||||
if value != self.value {
|
||||
let previousValue = self.value
|
||||
@@ -501,6 +555,8 @@ private final class SemanticStatusNodeCheckContext: SemanticStatusNodeStateConte
|
||||
return true
|
||||
}
|
||||
|
||||
var requestUpdate: () -> Void = {}
|
||||
|
||||
init(value: CGFloat, appearance: SemanticStatusNodeState.CheckAppearance?) {
|
||||
self.value = value
|
||||
self.appearance = appearance
|
||||
@@ -524,6 +580,10 @@ private final class SemanticStatusNodeCheckContext: SemanticStatusNodeStateConte
|
||||
return DrawingState(transitionFraction: transitionFraction, value: resolvedValue, appearance: self.appearance)
|
||||
}
|
||||
|
||||
func maskView() -> UIView? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func animate() {
|
||||
guard self.value < 1.0 else {
|
||||
return
|
||||
@@ -553,8 +613,15 @@ private extension SemanticStatusNodeState {
|
||||
default:
|
||||
preconditionFailure()
|
||||
}
|
||||
if let current = current as? SemanticStatusNodeIconContext, current.icon == icon {
|
||||
return current
|
||||
if let current = current as? SemanticStatusNodeIconContext {
|
||||
if current.icon == icon {
|
||||
return current
|
||||
} else if (current.icon == .play && icon == .pause) || (current.icon == .pause && icon == .play) {
|
||||
current.icon = icon
|
||||
return current
|
||||
} else {
|
||||
return SemanticStatusNodeIconContext(icon: icon)
|
||||
}
|
||||
} else {
|
||||
return SemanticStatusNodeIconContext(icon: icon)
|
||||
}
|
||||
@@ -874,6 +941,9 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
self.state = state
|
||||
let previousStateContext = self.stateContext
|
||||
self.stateContext = self.state.context(current: self.stateContext)
|
||||
self.stateContext.requestUpdate = { [weak self] in
|
||||
self?.setNeedsDisplay()
|
||||
}
|
||||
|
||||
if animated && previousStateContext !== self.stateContext {
|
||||
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: previousStateContext, previousAppearanceContext: nil, completion: completion)
|
||||
@@ -947,3 +1017,53 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
parameters.appearanceState.drawForeground(context: context, size: bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
private enum PlayPauseIconNodeState: Equatable {
|
||||
case play
|
||||
case pause
|
||||
}
|
||||
|
||||
private final class PlayPauseIconNode: ManagedAnimationNode {
|
||||
private let duration: Double = 0.35
|
||||
private var iconState: PlayPauseIconNodeState = .play
|
||||
|
||||
init() {
|
||||
super.init(size: CGSize(width: 36.0, height: 36.0))
|
||||
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.01))
|
||||
}
|
||||
|
||||
func enqueueState(_ state: PlayPauseIconNodeState, animated: Bool) {
|
||||
guard self.iconState != state else {
|
||||
return
|
||||
}
|
||||
|
||||
let previousState = self.iconState
|
||||
self.iconState = state
|
||||
|
||||
switch previousState {
|
||||
case .pause:
|
||||
switch state {
|
||||
case .play:
|
||||
if animated {
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 41, endFrame: 83), duration: self.duration))
|
||||
} else {
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.01))
|
||||
}
|
||||
case .pause:
|
||||
break
|
||||
}
|
||||
case .play:
|
||||
switch state {
|
||||
case .pause:
|
||||
if animated {
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 0, endFrame: 41), duration: self.duration))
|
||||
} else {
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 41, endFrame: 41), duration: 0.01))
|
||||
}
|
||||
case .play:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user