mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Sticker pack preview updates
This commit is contained in:
@@ -7,17 +7,20 @@ import AnimatedStickerNode
|
||||
import AppBundle
|
||||
|
||||
private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
private let location: CGPoint
|
||||
private let icon: TooltipScreen.Icon
|
||||
private let location: CGRect
|
||||
private let shouldDismissOnTouch: (CGPoint) -> Bool
|
||||
private let requestDismiss: () -> Void
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
private let backgroundNode: ASImageNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let arrowContainer: ASDisplayNode
|
||||
private let animatedStickerNode: AnimatedStickerNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
init(text: String, location: CGPoint, shouldDismissOnTouch: @escaping (CGPoint) -> Bool, requestDismiss: @escaping () -> Void) {
|
||||
init(text: String, icon: TooltipScreen.Icon, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> Bool, requestDismiss: @escaping () -> Void) {
|
||||
self.icon = icon
|
||||
self.location = location
|
||||
self.shouldDismissOnTouch = shouldDismissOnTouch
|
||||
self.requestDismiss = requestDismiss
|
||||
@@ -39,20 +42,31 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
context.fillPath()
|
||||
})
|
||||
|
||||
self.arrowContainer = ASDisplayNode()
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
|
||||
|
||||
self.animatedStickerNode = AnimatedStickerNode()
|
||||
if let path = getAppBundle().path(forResource: "ChatListFoldersTooltip", ofType: "json") {
|
||||
self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct)
|
||||
self.animatedStickerNode.automaticallyLoadFirstFrame = true
|
||||
switch icon {
|
||||
case .chatListPress:
|
||||
if let path = getAppBundle().path(forResource: "ChatListFoldersTooltip", ofType: "json") {
|
||||
self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct)
|
||||
self.animatedStickerNode.automaticallyLoadFirstFrame = true
|
||||
}
|
||||
case .info:
|
||||
if let path = getAppBundle().path(forResource: "anim_infotip", ofType: "json") {
|
||||
self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct)
|
||||
self.animatedStickerNode.automaticallyLoadFirstFrame = true
|
||||
}
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundNode.addSubnode(self.arrowNode)
|
||||
self.arrowContainer.addSubnode(self.arrowNode)
|
||||
self.backgroundNode.addSubnode(self.arrowContainer)
|
||||
self.containerNode.addSubnode(self.backgroundNode)
|
||||
self.containerNode.addSubnode(self.textNode)
|
||||
self.containerNode.addSubnode(self.animatedStickerNode)
|
||||
@@ -64,19 +78,48 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
let bottomInset: CGFloat = 10.0
|
||||
let contentInset: CGFloat = 9.0
|
||||
let contentVerticalInset: CGFloat = 11.0
|
||||
let animationSize = CGSize(width: 32.0, height: 32.0)
|
||||
let animationInset: CGFloat = (70 - animationSize.width) / 2.0
|
||||
let animationSize: CGSize
|
||||
let animationInset: CGFloat
|
||||
|
||||
switch self.icon {
|
||||
case .chatListPress:
|
||||
animationSize = CGSize(width: 32.0, height: 32.0)
|
||||
animationInset = (70.0 - animationSize.width) / 2.0
|
||||
case .info:
|
||||
animationSize = CGSize(width: 32.0, height: 32.0)
|
||||
animationInset = 0.0
|
||||
}
|
||||
|
||||
let animationSpacing: CGFloat = 8.0
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - contentInset * 2.0 - sideInset * 2.0 - animationSize.width - animationSpacing, height: .greatestFiniteMagnitude))
|
||||
|
||||
let backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: self.location.y - bottomInset - backgroundHeight), size: CGSize(width: layout.size.width - sideInset * 2.0, height: backgroundHeight))
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: self.location.minY - bottomInset - backgroundHeight), size: CGSize(width: layout.size.width - sideInset * 2.0, height: backgroundHeight))
|
||||
var invertArrow = false
|
||||
if backgroundFrame.minY < layout.insets(options: .statusBar).top {
|
||||
backgroundFrame.origin.y = self.location.maxY + bottomInset
|
||||
invertArrow = true
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.containerNode, frame: backgroundFrame)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
if let image = self.arrowNode.image {
|
||||
let arrowSize = image.size
|
||||
let arrowCenterX = self.location.x
|
||||
transition.updateFrame(node: self.arrowNode, frame: CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: backgroundFrame.height), size: arrowSize))
|
||||
let arrowCenterX = self.location.midX
|
||||
|
||||
let arrowFrame: CGRect
|
||||
|
||||
if invertArrow {
|
||||
arrowFrame = CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: -arrowSize.height), size: arrowSize)
|
||||
} else {
|
||||
arrowFrame = CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: backgroundFrame.height), size: arrowSize)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.arrowContainer, frame: arrowFrame)
|
||||
|
||||
ContainedViewLayoutTransition.immediate.updateTransformScale(node: self.arrowContainer, scale: CGPoint(x: 1.0, y: invertArrow ? -1.0 : 1.0))
|
||||
|
||||
self.arrowNode.frame = CGRect(origin: CGPoint(), size: arrowFrame.size)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentInset + animationSize.width + animationSpacing, y: floor((backgroundHeight - textSize.height) / 2.0)), size: textSize))
|
||||
@@ -103,10 +146,18 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
|
||||
func animateIn() {
|
||||
self.containerNode.layer.animateSpring(from: NSNumber(value: Float(0.01)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6)
|
||||
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: self.arrowNode.frame.midX - self.containerNode.bounds.width / 2.0, y: self.arrowNode.frame.maxY - self.containerNode.bounds.height / 2.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.6, additive: true)
|
||||
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: self.arrowContainer.frame.maxY - self.containerNode.bounds.height / 2.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.6, additive: true)
|
||||
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6, execute: { [weak self] in
|
||||
let animationDelay: Double
|
||||
switch self.icon {
|
||||
case .chatListPress:
|
||||
animationDelay = 0.6
|
||||
case .info:
|
||||
animationDelay = 0.2
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + animationDelay, execute: { [weak self] in
|
||||
self?.animatedStickerNode.visibility = true
|
||||
})
|
||||
}
|
||||
@@ -116,13 +167,19 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
completion()
|
||||
})
|
||||
self.containerNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: self.arrowNode.frame.midX - self.containerNode.bounds.width / 2.0, y: self.arrowNode.frame.maxY - self.containerNode.bounds.height / 2.0), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: self.arrowContainer.frame.maxY - self.containerNode.bounds.height / 2.0), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
public final class TooltipScreen: ViewController {
|
||||
public enum Icon {
|
||||
case info
|
||||
case chatListPress
|
||||
}
|
||||
|
||||
private let text: String
|
||||
private let location: CGPoint
|
||||
private let icon: TooltipScreen.Icon
|
||||
private let location: CGRect
|
||||
private let shouldDismissOnTouch: (CGPoint) -> Bool
|
||||
|
||||
private var controllerNode: TooltipScreenNode {
|
||||
@@ -132,8 +189,9 @@ public final class TooltipScreen: ViewController {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var isDismissed: Bool = false
|
||||
|
||||
public init(text: String, location: CGPoint, shouldDismissOnTouch: @escaping (CGPoint) -> Bool) {
|
||||
public init(text: String, icon: TooltipScreen.Icon, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> Bool) {
|
||||
self.text = text
|
||||
self.icon = icon
|
||||
self.location = location
|
||||
self.shouldDismissOnTouch = shouldDismissOnTouch
|
||||
|
||||
@@ -151,13 +209,13 @@ public final class TooltipScreen: ViewController {
|
||||
|
||||
self.controllerNode.animateIn()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5.0, execute: { [weak self] in
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 8.0, execute: { [weak self] in
|
||||
self?.dismiss()
|
||||
})
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = TooltipScreenNode(text: self.text, location: self.location, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
|
||||
self.displayNode = TooltipScreenNode(text: self.text, icon: self.icon, location: self.location, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user