mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Assorted fixes
This commit is contained in:
@@ -5,13 +5,17 @@ import Display
|
||||
import TelegramPresentationData
|
||||
import AnimatedStickerNode
|
||||
import AppBundle
|
||||
import SyncCore
|
||||
import TelegramCore
|
||||
import TextFormat
|
||||
|
||||
private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
private let icon: TooltipScreen.Icon
|
||||
private let icon: TooltipScreen.Icon?
|
||||
private let location: CGRect
|
||||
private let shouldDismissOnTouch: (CGPoint) -> Bool
|
||||
private let shouldDismissOnTouch: (CGPoint) -> TooltipScreen.DismissOnTouch
|
||||
private let requestDismiss: () -> Void
|
||||
|
||||
private let scrollingContainer: ASDisplayNode
|
||||
private let containerNode: ASDisplayNode
|
||||
private let backgroundNode: ASImageNode
|
||||
private let arrowNode: ASImageNode
|
||||
@@ -19,7 +23,9 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
private let animatedStickerNode: AnimatedStickerNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
init(text: String, icon: TooltipScreen.Icon, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> Bool, requestDismiss: @escaping () -> Void) {
|
||||
private var isArrowInverted: Bool = false
|
||||
|
||||
init(text: String, textEntities: [MessageTextEntity], icon: TooltipScreen.Icon?, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openUrl: @escaping (String) -> Void) {
|
||||
self.icon = icon
|
||||
self.location = location
|
||||
self.shouldDismissOnTouch = shouldDismissOnTouch
|
||||
@@ -29,6 +35,8 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
|
||||
let fillColor = UIColor(white: 0.0, alpha: 0.8)
|
||||
|
||||
self.scrollingContainer = ASDisplayNode()
|
||||
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.image = generateAdjustedStretchableFilledCircleImage(diameter: 15.0, color: fillColor)
|
||||
|
||||
@@ -47,10 +55,13 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
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.textNode.attributedText = stringWithAppliedEntities(text, entities: textEntities, baseColor: .white, linkColor: .white, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), underlineLinks: true, external: false)
|
||||
|
||||
self.animatedStickerNode = AnimatedStickerNode()
|
||||
switch icon {
|
||||
case .none:
|
||||
break
|
||||
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)
|
||||
@@ -70,36 +81,80 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
self.containerNode.addSubnode(self.backgroundNode)
|
||||
self.containerNode.addSubnode(self.textNode)
|
||||
self.containerNode.addSubnode(self.animatedStickerNode)
|
||||
self.addSubnode(self.containerNode)
|
||||
self.scrollingContainer.addSubnode(self.containerNode)
|
||||
self.addSubnode(self.scrollingContainer)
|
||||
|
||||
self.textNode.linkHighlightColor = UIColor.white.withAlphaComponent(0.5)
|
||||
self.textNode.highlightAttributeAction = { attributes in
|
||||
let highlightedAttributes = [
|
||||
TelegramTextAttributes.URL,
|
||||
TelegramTextAttributes.PeerMention,
|
||||
TelegramTextAttributes.PeerTextMention,
|
||||
TelegramTextAttributes.BotCommand,
|
||||
TelegramTextAttributes.Hashtag,
|
||||
TelegramTextAttributes.Timecode
|
||||
]
|
||||
|
||||
for attribute in highlightedAttributes {
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: attribute)] {
|
||||
return NSAttributedString.Key(rawValue: attribute)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
self.textNode.tapAttributeAction = { [weak self] attributes in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
openUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.scrollingContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
let sideInset: CGFloat = 13.0
|
||||
let bottomInset: CGFloat = 10.0
|
||||
let contentInset: CGFloat = 9.0
|
||||
let contentVerticalInset: CGFloat = 11.0
|
||||
let animationSize: CGSize
|
||||
let animationInset: CGFloat
|
||||
let animationSpacing: CGFloat
|
||||
|
||||
switch self.icon {
|
||||
case .none:
|
||||
animationSize = CGSize()
|
||||
animationInset = 0.0
|
||||
animationSpacing = 0.0
|
||||
case .chatListPress:
|
||||
animationSize = CGSize(width: 32.0, height: 32.0)
|
||||
animationInset = (70.0 - animationSize.width) / 2.0
|
||||
animationSpacing = 8.0
|
||||
case .info:
|
||||
animationSize = CGSize(width: 32.0, height: 32.0)
|
||||
animationInset = 0.0
|
||||
animationSpacing = 8.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 backgroundWidth = textSize.width + contentInset * 2.0 + sideInset * 2.0 + animationSize.width + animationSpacing
|
||||
let backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
||||
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 backgroundFrame = CGRect(origin: CGPoint(x: self.location.midX - backgroundWidth / 2.0, y: self.location.minY - bottomInset - backgroundHeight), size: CGSize(width: backgroundWidth, height: backgroundHeight))
|
||||
if backgroundFrame.minX < sideInset {
|
||||
backgroundFrame.origin.x = sideInset
|
||||
}
|
||||
if backgroundFrame.maxX > layout.size.width - sideInset {
|
||||
backgroundFrame.origin.x = layout.size.width - sideInset - backgroundFrame.width
|
||||
}
|
||||
var invertArrow = false
|
||||
if backgroundFrame.minY < layout.insets(options: .statusBar).top {
|
||||
backgroundFrame.origin.y = self.location.maxY + bottomInset
|
||||
invertArrow = true
|
||||
}
|
||||
self.isArrowInverted = invertArrow
|
||||
|
||||
transition.updateFrame(node: self.containerNode, frame: backgroundFrame)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
@@ -115,7 +170,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
arrowFrame = CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: backgroundFrame.height), size: arrowSize)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.arrowContainer, frame: arrowFrame)
|
||||
transition.updateFrame(node: self.arrowContainer, frame: arrowFrame.offsetBy(dx: -backgroundFrame.minX, dy: 0.0))
|
||||
|
||||
ContainedViewLayoutTransition.immediate.updateTransformScale(node: self.arrowContainer, scale: CGPoint(x: 1.0, y: invertArrow ? -1.0 : 1.0))
|
||||
|
||||
@@ -130,13 +185,23 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if let event = event {
|
||||
if let result = self.textNode.hitTest(self.view.convert(point, to: self.textNode.view), with: event) {
|
||||
return result
|
||||
}
|
||||
|
||||
var eventIsPresses = false
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
eventIsPresses = event.type == .presses
|
||||
}
|
||||
if event.type == .touches || eventIsPresses {
|
||||
if self.shouldDismissOnTouch(point) {
|
||||
switch self.shouldDismissOnTouch(point) {
|
||||
case .ignore:
|
||||
break
|
||||
case let .dismiss(consume):
|
||||
self.requestDismiss()
|
||||
if consume {
|
||||
return self.view
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -146,7 +211,8 @@ 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.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)
|
||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - 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)
|
||||
|
||||
let animationDelay: Double
|
||||
@@ -155,6 +221,8 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
animationDelay = 0.6
|
||||
case .info:
|
||||
animationDelay = 0.2
|
||||
case .none:
|
||||
animationDelay = 0.0
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + animationDelay, execute: { [weak self] in
|
||||
@@ -167,7 +235,13 @@ 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.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)
|
||||
|
||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
|
||||
func addRelativeScrollingOffset(_ value: CGFloat) {
|
||||
self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,10 +251,17 @@ public final class TooltipScreen: ViewController {
|
||||
case chatListPress
|
||||
}
|
||||
|
||||
public enum DismissOnTouch {
|
||||
case ignore
|
||||
case dismiss(consume: Bool)
|
||||
}
|
||||
|
||||
private let text: String
|
||||
private let icon: TooltipScreen.Icon
|
||||
private let textEntities: [MessageTextEntity]
|
||||
private let icon: TooltipScreen.Icon?
|
||||
private let location: CGRect
|
||||
private let shouldDismissOnTouch: (CGPoint) -> Bool
|
||||
private let shouldDismissOnTouch: (CGPoint) -> TooltipScreen.DismissOnTouch
|
||||
private let openUrl: (String) -> Void
|
||||
|
||||
private var controllerNode: TooltipScreenNode {
|
||||
return self.displayNode as! TooltipScreenNode
|
||||
@@ -189,11 +270,15 @@ public final class TooltipScreen: ViewController {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var isDismissed: Bool = false
|
||||
|
||||
public init(text: String, icon: TooltipScreen.Icon, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> Bool) {
|
||||
public var becameDismissed: ((TooltipScreen) -> Void)?
|
||||
|
||||
public init(text: String, textEntities: [MessageTextEntity] = [], icon: TooltipScreen.Icon?, location: CGRect, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openUrl: @escaping (String) -> Void = { _ in }) {
|
||||
self.text = text
|
||||
self.textEntities = textEntities
|
||||
self.icon = icon
|
||||
self.location = location
|
||||
self.shouldDismissOnTouch = shouldDismissOnTouch
|
||||
self.openUrl = openUrl
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@@ -209,18 +294,18 @@ public final class TooltipScreen: ViewController {
|
||||
|
||||
self.controllerNode.animateIn()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 8.0, execute: { [weak self] in
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 20.0, execute: { [weak self] in
|
||||
self?.dismiss()
|
||||
})
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = TooltipScreenNode(text: self.text, icon: self.icon, location: self.location, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
|
||||
self.displayNode = TooltipScreenNode(text: self.text, textEntities: self.textEntities, icon: self.icon, location: self.location, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.dismiss()
|
||||
})
|
||||
}, openUrl: self.openUrl)
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
@@ -237,6 +322,10 @@ public final class TooltipScreen: ViewController {
|
||||
self.controllerNode.updateLayout(layout: layout, transition: transition)
|
||||
}
|
||||
|
||||
public func addRelativeScrollingOffset(_ value: CGFloat) {
|
||||
self.controllerNode.addRelativeScrollingOffset(value)
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
if self.isDismissed {
|
||||
return
|
||||
@@ -246,7 +335,9 @@ public final class TooltipScreen: ViewController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let becameDismissed = strongSelf.becameDismissed
|
||||
strongSelf.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
becameDismissed?(strongSelf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user