mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
236 lines
12 KiB
Swift
236 lines
12 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import TelegramPresentationData
|
|
import ChatPresentationInterfaceState
|
|
import ShimmerEffect
|
|
|
|
private let buttonFont = Font.semibold(14.0)
|
|
|
|
public final class ChatMessageAttachedContentButtonNode: HighlightTrackingButtonNode {
|
|
private let textNode: TextNode
|
|
private let iconNode: ASImageNode
|
|
private let highlightedTextNode: TextNode
|
|
private let backgroundNode: ASImageNode
|
|
private let shimmerEffectNode: ShimmerEffectForegroundNode
|
|
|
|
private var regularImage: UIImage?
|
|
private var highlightedImage: UIImage?
|
|
private var regularIconImage: UIImage?
|
|
private var highlightedIconImage: UIImage?
|
|
|
|
public var pressed: (() -> Void)?
|
|
|
|
private var titleColor: UIColor?
|
|
|
|
public init() {
|
|
self.textNode = TextNode()
|
|
self.textNode.isUserInteractionEnabled = false
|
|
self.highlightedTextNode = TextNode()
|
|
self.highlightedTextNode.isUserInteractionEnabled = false
|
|
|
|
self.shimmerEffectNode = ShimmerEffectForegroundNode()
|
|
self.shimmerEffectNode.cornerRadius = 5.0
|
|
|
|
self.backgroundNode = ASImageNode()
|
|
self.backgroundNode.isLayerBacked = true
|
|
self.backgroundNode.displayWithoutProcessing = true
|
|
self.backgroundNode.displaysAsynchronously = false
|
|
|
|
self.iconNode = ASImageNode()
|
|
self.iconNode.isLayerBacked = true
|
|
self.iconNode.displayWithoutProcessing = true
|
|
self.iconNode.displaysAsynchronously = false
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.shimmerEffectNode)
|
|
self.addSubnode(self.backgroundNode)
|
|
self.addSubnode(self.textNode)
|
|
self.addSubnode(self.highlightedTextNode)
|
|
self.highlightedTextNode.isHidden = true
|
|
|
|
self.highligthedChanged = { [weak self] highlighted in
|
|
if let strongSelf = self {
|
|
if highlighted {
|
|
strongSelf.backgroundNode.image = strongSelf.highlightedImage
|
|
strongSelf.iconNode.image = strongSelf.highlightedIconImage
|
|
strongSelf.textNode.isHidden = true
|
|
strongSelf.highlightedTextNode.isHidden = false
|
|
|
|
let scale = (strongSelf.bounds.width - 10.0) / strongSelf.bounds.width
|
|
strongSelf.layer.animateScale(from: 1.0, to: scale, duration: 0.15, removeOnCompletion: false)
|
|
} else {
|
|
if let presentationLayer = strongSelf.layer.presentation() {
|
|
strongSelf.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
|
|
}
|
|
if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) {
|
|
strongSelf.view.addSubview(snapshot)
|
|
|
|
snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
|
snapshot.removeFromSuperview()
|
|
})
|
|
}
|
|
|
|
strongSelf.backgroundNode.image = strongSelf.regularImage
|
|
strongSelf.iconNode.image = strongSelf.regularIconImage
|
|
strongSelf.textNode.isHidden = false
|
|
strongSelf.highlightedTextNode.isHidden = true
|
|
}
|
|
}
|
|
}
|
|
|
|
self.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
|
}
|
|
|
|
@objc private func buttonPressed() {
|
|
self.pressed?()
|
|
}
|
|
|
|
public func startShimmering() {
|
|
guard let titleColor = self.titleColor else {
|
|
return
|
|
}
|
|
self.shimmerEffectNode.isHidden = false
|
|
self.shimmerEffectNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
|
|
let backgroundFrame = self.backgroundNode.frame
|
|
self.shimmerEffectNode.frame = backgroundFrame
|
|
self.shimmerEffectNode.updateAbsoluteRect(CGRect(origin: .zero, size: backgroundFrame.size), within: backgroundFrame.size)
|
|
self.shimmerEffectNode.update(backgroundColor: .clear, foregroundColor: titleColor.withAlphaComponent(0.3), horizontal: true, effectSize: nil, globalTimeOffset: false, duration: nil)
|
|
}
|
|
|
|
public func stopShimmering() {
|
|
self.shimmerEffectNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in
|
|
self?.shimmerEffectNode.isHidden = true
|
|
})
|
|
}
|
|
|
|
public static func asyncLayout(_ current: ChatMessageAttachedContentButtonNode?) -> (_ width: CGFloat, _ regularImage: UIImage?, _ highlightedImage: UIImage?, _ iconImage: UIImage?, _ highlightedIconImage: UIImage?, _ cornerIcon: Bool, _ title: String, _ titleColor: UIColor, _ highlightedTitleColor: UIColor, _ inProgress: Bool) -> (CGFloat, (CGFloat, CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode)) {
|
|
let previousRegularImage = current?.regularImage
|
|
let previousHighlightedImage = current?.highlightedImage
|
|
let previousRegularIconImage = current?.regularIconImage
|
|
let previousHighlightedIconImage = current?.highlightedIconImage
|
|
|
|
let maybeMakeTextLayout = (current?.textNode).flatMap(TextNode.asyncLayout)
|
|
let maybeMakeHighlightedTextLayout = (current?.highlightedTextNode).flatMap(TextNode.asyncLayout)
|
|
|
|
return { width, regularImage, highlightedImage, iconImage, highlightedIconImage, cornerIcon, title, titleColor, highlightedTitleColor, inProgress in
|
|
let targetNode: ChatMessageAttachedContentButtonNode
|
|
if let current = current {
|
|
targetNode = current
|
|
} else {
|
|
targetNode = ChatMessageAttachedContentButtonNode()
|
|
}
|
|
|
|
let makeTextLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode)
|
|
if let maybeMakeTextLayout = maybeMakeTextLayout {
|
|
makeTextLayout = maybeMakeTextLayout
|
|
} else {
|
|
makeTextLayout = TextNode.asyncLayout(targetNode.textNode)
|
|
}
|
|
|
|
let makeHighlightedTextLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode)
|
|
if let maybeMakeHighlightedTextLayout = maybeMakeHighlightedTextLayout {
|
|
makeHighlightedTextLayout = maybeMakeHighlightedTextLayout
|
|
} else {
|
|
makeHighlightedTextLayout = TextNode.asyncLayout(targetNode.highlightedTextNode)
|
|
}
|
|
|
|
var updatedRegularImage: UIImage?
|
|
if regularImage !== previousRegularImage {
|
|
updatedRegularImage = regularImage
|
|
}
|
|
|
|
var updatedHighlightedImage: UIImage?
|
|
if highlightedImage !== previousHighlightedImage {
|
|
updatedHighlightedImage = highlightedImage
|
|
}
|
|
|
|
var updatedRegularIconImage: UIImage?
|
|
if iconImage !== previousRegularIconImage {
|
|
updatedRegularIconImage = iconImage
|
|
}
|
|
|
|
var updatedHighlightedIconImage: UIImage?
|
|
if highlightedIconImage !== previousHighlightedIconImage {
|
|
updatedHighlightedIconImage = highlightedIconImage
|
|
}
|
|
|
|
var iconWidth: CGFloat = 0.0
|
|
if let iconImage = iconImage {
|
|
iconWidth = iconImage.size.width + 5.0
|
|
}
|
|
|
|
let labelInset: CGFloat = 8.0
|
|
|
|
let (textSize, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: buttonFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(1.0, width - labelInset * 2.0 - iconWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .left, cutout: nil, insets: UIEdgeInsets()))
|
|
|
|
let (_, highlightedTextApply) = makeHighlightedTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: buttonFont, textColor: highlightedTitleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(1.0, width - labelInset * 2.0), height: CGFloat.greatestFiniteMagnitude), alignment: .left, cutout: nil, insets: UIEdgeInsets()))
|
|
|
|
return (textSize.size.width + labelInset * 2.0, { refinedWidth, refinedHeight in
|
|
let size = CGSize(width: refinedWidth, height: refinedHeight)
|
|
return (size, {
|
|
targetNode.accessibilityLabel = title
|
|
|
|
//targetNode.borderColor = UIColor.red.cgColor
|
|
//targetNode.borderWidth = 1.0
|
|
|
|
targetNode.titleColor = titleColor
|
|
|
|
if let updatedRegularImage = updatedRegularImage {
|
|
targetNode.regularImage = updatedRegularImage
|
|
if !targetNode.textNode.isHidden {
|
|
targetNode.backgroundNode.image = updatedRegularImage
|
|
}
|
|
}
|
|
if let updatedHighlightedImage = updatedHighlightedImage {
|
|
targetNode.highlightedImage = updatedHighlightedImage
|
|
if targetNode.textNode.isHidden {
|
|
targetNode.backgroundNode.image = updatedHighlightedImage
|
|
}
|
|
}
|
|
if let updatedRegularIconImage = updatedRegularIconImage {
|
|
targetNode.regularIconImage = updatedRegularIconImage
|
|
if !targetNode.textNode.isHidden {
|
|
targetNode.iconNode.image = updatedRegularIconImage
|
|
}
|
|
}
|
|
if let updatedHighlightedIconImage = updatedHighlightedIconImage {
|
|
targetNode.highlightedIconImage = updatedHighlightedIconImage
|
|
if targetNode.iconNode.isHidden {
|
|
targetNode.iconNode.image = updatedHighlightedIconImage
|
|
}
|
|
}
|
|
|
|
let _ = textApply()
|
|
let _ = highlightedTextApply()
|
|
|
|
let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: refinedWidth, height: size.height))
|
|
var textFrame = CGRect(origin: CGPoint(x: floor((refinedWidth - textSize.size.width) / 2.0), y: floor((backgroundFrame.height - textSize.size.height) / 2.0)), size: textSize.size)
|
|
targetNode.backgroundNode.frame = backgroundFrame
|
|
if let image = targetNode.iconNode.image {
|
|
if cornerIcon {
|
|
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 5.0, y: 5.0), size: image.size)
|
|
} else {
|
|
textFrame.origin.x += floor(image.size.width / 2.0)
|
|
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: textFrame.minX - image.size.width - 5.0, y: textFrame.minY + floorToScreenPixels((textFrame.height - image.size.height) * 0.5)), size: image.size)
|
|
}
|
|
if targetNode.iconNode.supernode == nil {
|
|
targetNode.addSubnode(targetNode.iconNode)
|
|
}
|
|
} else if targetNode.iconNode.supernode != nil {
|
|
targetNode.iconNode.removeFromSupernode()
|
|
}
|
|
|
|
targetNode.textNode.frame = textFrame
|
|
targetNode.highlightedTextNode.frame = targetNode.textNode.frame
|
|
|
|
return targetNode
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|