mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
[Temp] custom emoji layouts
This commit is contained in:
@@ -19,6 +19,7 @@ import AnimationCache
|
||||
import LottieAnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import EmojiTextAttachmentView
|
||||
import TextNodeWithEntities
|
||||
|
||||
private final class CachedChatMessageText {
|
||||
let text: String
|
||||
@@ -46,27 +47,8 @@ private final class CachedChatMessageText {
|
||||
}
|
||||
}
|
||||
|
||||
private final class InlineStickerItem: Hashable {
|
||||
let emoji: ChatTextInputTextCustomEmojiAttribute
|
||||
|
||||
init(emoji: ChatTextInputTextCustomEmojiAttribute) {
|
||||
self.emoji = emoji
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(emoji.fileId)
|
||||
}
|
||||
|
||||
static func ==(lhs: InlineStickerItem, rhs: InlineStickerItem) -> Bool {
|
||||
if lhs.emoji.fileId != rhs.emoji.fileId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private let textNode: TextNode
|
||||
private let textNode: TextNodeWithEntities
|
||||
private var spoilerTextNode: TextNode?
|
||||
private var dustNode: InvisibleInkDustNode?
|
||||
|
||||
@@ -76,42 +58,27 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var textSelectionNode: TextSelectionNode?
|
||||
|
||||
private var textHighlightingNodes: [LinkHighlightingNode] = []
|
||||
private var inlineStickerItemLayers: [InlineStickerItemLayer.Key: InlineStickerItemLayer] = [:]
|
||||
|
||||
private var cachedChatMessageText: CachedChatMessageText?
|
||||
|
||||
private var isVisibleForAnimations: Bool {
|
||||
return self.visibility != .none
|
||||
}
|
||||
|
||||
override var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
if !self.inlineStickerItemLayers.isEmpty {
|
||||
if oldValue != self.visibility {
|
||||
for (_, itemLayer) in self.inlineStickerItemLayers {
|
||||
let isItemVisible: Bool
|
||||
switch self.visibility {
|
||||
case .none:
|
||||
isItemVisible = false
|
||||
case let .visible(_, subRect):
|
||||
var subRect = subRect
|
||||
subRect.origin.x = 0.0
|
||||
subRect.size.width = 10000.0
|
||||
if itemLayer.frame.intersects(subRect) {
|
||||
isItemVisible = true
|
||||
} else {
|
||||
isItemVisible = false
|
||||
}
|
||||
}
|
||||
itemLayer.isVisibleForAnimations = isItemVisible
|
||||
}
|
||||
if oldValue != self.visibility {
|
||||
switch self.visibility {
|
||||
case .none:
|
||||
self.textNode.visibilityRect = nil
|
||||
case let .visible(_, subRect):
|
||||
var subRect = subRect
|
||||
subRect.origin.x = 0.0
|
||||
subRect.size.width = 10000.0
|
||||
self.textNode.visibilityRect = subRect
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init() {
|
||||
self.textNode = TextNode()
|
||||
self.textNode = TextNodeWithEntities()
|
||||
|
||||
self.statusNode = ChatMessageDateAndStatusNode()
|
||||
|
||||
@@ -119,11 +86,11 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.contentMode = .topLeft
|
||||
self.textNode.contentsScale = UIScreenScale
|
||||
self.textNode.displaysAsynchronously = true
|
||||
self.addSubnode(self.textNode)
|
||||
self.textNode.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.textNode.contentMode = .topLeft
|
||||
self.textNode.textNode.contentsScale = UIScreenScale
|
||||
self.textNode.textNode.displaysAsynchronously = true
|
||||
self.addSubnode(self.textNode.textNode)
|
||||
self.addSubnode(self.textAccessibilityOverlayNode)
|
||||
|
||||
self.textAccessibilityOverlayNode.openUrl = { [weak self] url in
|
||||
@@ -152,7 +119,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||
let textLayout = TextNode.asyncLayout(self.textNode)
|
||||
let textLayout = TextNodeWithEntities.asyncLayout(self.textNode)
|
||||
let spoilerTextLayout = TextNode.asyncLayout(self.spoilerTextNode)
|
||||
let statusLayout = self.statusNode.asyncLayout()
|
||||
|
||||
@@ -354,7 +321,6 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let currentDict = updatedString.attributes(at: range.lowerBound, effectiveRange: nil)
|
||||
var updatedAttributes: [NSAttributedString.Key: Any] = currentDict
|
||||
updatedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.clear.cgColor
|
||||
updatedAttributes[NSAttributedString.Key("Attribute__EmbeddedItem")] = InlineStickerItem(emoji: ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId))
|
||||
updatedAttributes[ChatTextInputAttributes.customEmoji] = ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId)
|
||||
|
||||
let insertString = NSAttributedString(string: updatedString.attributedSubstring(from: range).string, attributes: updatedAttributes)
|
||||
@@ -445,29 +411,29 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.cachedChatMessageText = updatedCachedChatMessageText
|
||||
}
|
||||
|
||||
let cachedLayout = strongSelf.textNode.cachedLayout
|
||||
let cachedLayout = strongSelf.textNode.textNode.cachedLayout
|
||||
|
||||
if case .System = animation {
|
||||
if let cachedLayout = cachedLayout {
|
||||
if !cachedLayout.areLinesEqual(to: textLayout) {
|
||||
if let textContents = strongSelf.textNode.contents {
|
||||
if let textContents = strongSelf.textNode.textNode.contents {
|
||||
let fadeNode = ASDisplayNode()
|
||||
fadeNode.displaysAsynchronously = false
|
||||
fadeNode.contents = textContents
|
||||
fadeNode.frame = strongSelf.textNode.frame
|
||||
fadeNode.frame = strongSelf.textNode.textNode.frame
|
||||
fadeNode.isLayerBacked = true
|
||||
strongSelf.addSubnode(fadeNode)
|
||||
fadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak fadeNode] _ in
|
||||
fadeNode?.removeFromSupernode()
|
||||
})
|
||||
strongSelf.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
strongSelf.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = textApply()
|
||||
animation.animator.updateFrame(layer: strongSelf.textNode.layer, frame: textFrame, completion: nil)
|
||||
let _ = textApply(TextNodeWithEntities.Arguments(context: item.context, cache: item.controllerInteraction.presentationContext.animationCache, renderer: item.controllerInteraction.presentationContext.animationRenderer, placeholderColor: messageTheme.mediaPlaceholderColor))
|
||||
animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: textFrame, completion: nil)
|
||||
|
||||
if let (_, spoilerTextApply) = spoilerTextLayoutAndApply {
|
||||
let spoilerTextNode = spoilerTextApply()
|
||||
@@ -514,8 +480,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
strongSelf.textAccessibilityOverlayNode.frame = textFrame
|
||||
strongSelf.textAccessibilityOverlayNode.cachedLayout = textLayout
|
||||
|
||||
strongSelf.updateInlineStickers(context: item.context, cache: item.controllerInteraction.presentationContext.animationCache, renderer: item.controllerInteraction.presentationContext.animationRenderer, textLayout: textLayout, placeholderColor: messageTheme.mediaPlaceholderColor)
|
||||
|
||||
|
||||
if let statusSizeAndApply = statusSizeAndApply {
|
||||
animation.animator.updateFrame(layer: strongSelf.statusNode.layer, frame: CGRect(origin: CGPoint(x: textFrameWithoutInsets.minX, y: textFrameWithoutInsets.maxY), size: statusSizeAndApply.0), completion: nil)
|
||||
@@ -546,73 +511,29 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateInlineStickers(context: AccountContext, cache: AnimationCache, renderer: MultiAnimationRenderer, textLayout: TextNodeLayout?, placeholderColor: UIColor) {
|
||||
var nextIndexById: [Int64: Int] = [:]
|
||||
var validIds: [InlineStickerItemLayer.Key] = []
|
||||
|
||||
if let textLayout = textLayout {
|
||||
for item in textLayout.embeddedItems {
|
||||
if let stickerItem = item.value as? InlineStickerItem {
|
||||
let index: Int
|
||||
if let currentNext = nextIndexById[stickerItem.emoji.fileId] {
|
||||
index = currentNext
|
||||
} else {
|
||||
index = 0
|
||||
}
|
||||
nextIndexById[stickerItem.emoji.fileId] = index + 1
|
||||
let id = InlineStickerItemLayer.Key(id: stickerItem.emoji.fileId, index: index)
|
||||
validIds.append(id)
|
||||
|
||||
let itemLayer: InlineStickerItemLayer
|
||||
if let current = self.inlineStickerItemLayers[id] {
|
||||
itemLayer = current
|
||||
} else {
|
||||
itemLayer = InlineStickerItemLayer(context: context, groupId: "inlineEmoji", attemptSynchronousLoad: false, emoji: stickerItem.emoji, cache: cache, renderer: renderer, placeholderColor: placeholderColor)
|
||||
self.inlineStickerItemLayers[id] = itemLayer
|
||||
self.textNode.layer.addSublayer(itemLayer)
|
||||
itemLayer.isVisibleForAnimations = self.isVisibleForAnimations
|
||||
}
|
||||
|
||||
itemLayer.frame = CGRect(origin: item.rect.offsetBy(dx: textLayout.insets.left, dy: textLayout.insets.top + 0.0).center, size: CGSize()).insetBy(dx: -12.0, dy: -12.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var removeKeys: [InlineStickerItemLayer.Key] = []
|
||||
for (key, itemLayer) in self.inlineStickerItemLayers {
|
||||
if !validIds.contains(key) {
|
||||
removeKeys.append(key)
|
||||
itemLayer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
for key in removeKeys {
|
||||
self.inlineStickerItemLayers.removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
override func animateInsertion(_ currentTimestamp: Double, duration: Double) {
|
||||
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.textNode.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.statusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
|
||||
let textNodeFrame = self.textNode.frame
|
||||
if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
let textNodeFrame = self.textNode.textNode.frame
|
||||
if let (index, attributes) = self.textNode.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !(self.dustNode?.isRevealed ?? true) {
|
||||
return .none
|
||||
} else if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
var concealed = true
|
||||
if let (attributeText, fullText) = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||
if let (attributeText, fullText) = self.textNode.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||
concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText)
|
||||
}
|
||||
return .url(url: url, concealed: concealed)
|
||||
@@ -669,8 +590,8 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var rects: [CGRect]?
|
||||
var spoilerRects: [CGRect]?
|
||||
if let point = point {
|
||||
let textNodeFrame = self.textNode.frame
|
||||
if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
let textNodeFrame = self.textNode.textNode.frame
|
||||
if let (index, attributes) = self.textNode.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
let possibleNames: [String] = [
|
||||
TelegramTextAttributes.URL,
|
||||
TelegramTextAttributes.PeerMention,
|
||||
@@ -682,12 +603,12 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
]
|
||||
for name in possibleNames {
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: name)] {
|
||||
rects = self.textNode.attributeRects(name: name, at: index)
|
||||
rects = self.textNode.textNode.attributeRects(name: name, at: index)
|
||||
break
|
||||
}
|
||||
}
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)] {
|
||||
spoilerRects = self.textNode.attributeRects(name: TelegramTextAttributes.Spoiler, at: index)
|
||||
spoilerRects = self.textNode.textNode.attributeRects(name: TelegramTextAttributes.Spoiler, at: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -701,9 +622,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
} else {
|
||||
linkHighlightingNode = LinkHighlightingNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.linkHighlightColor : item.presentationData.theme.theme.chat.message.outgoing.linkHighlightColor)
|
||||
self.linkHighlightingNode = linkHighlightingNode
|
||||
self.insertSubnode(linkHighlightingNode, belowSubnode: self.textNode)
|
||||
self.insertSubnode(linkHighlightingNode, belowSubnode: self.textNode.textNode)
|
||||
}
|
||||
linkHighlightingNode.frame = self.textNode.frame
|
||||
linkHighlightingNode.frame = self.textNode.textNode.frame
|
||||
linkHighlightingNode.updateRects(rects)
|
||||
} else if let linkHighlightingNode = self.linkHighlightingNode {
|
||||
self.linkHighlightingNode = nil
|
||||
@@ -716,16 +637,16 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
override func peekPreviewContent(at point: CGPoint) -> (Message, ChatMessagePeekPreviewContent)? {
|
||||
if let item = self.item {
|
||||
let textNodeFrame = self.textNode.frame
|
||||
if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
let textNodeFrame = self.textNode.textNode.frame
|
||||
if let (index, attributes) = self.textNode.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
if let value = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
if let rects = self.textNode.attributeRects(name: TelegramTextAttributes.URL, at: index), !rects.isEmpty {
|
||||
if let rects = self.textNode.textNode.attributeRects(name: TelegramTextAttributes.URL, at: index), !rects.isEmpty {
|
||||
var rect = rects[0]
|
||||
for i in 1 ..< rects.count {
|
||||
rect = rect.union(rects[i])
|
||||
}
|
||||
var concealed = true
|
||||
if let (attributeText, fullText) = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||
if let (attributeText, fullText) = self.textNode.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||
concealed = !doesUrlMatchText(url: value, text: attributeText, fullText: fullText)
|
||||
}
|
||||
return (item.message, .url(self, rect, value, concealed))
|
||||
@@ -742,7 +663,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
let rectsSet: [[CGRect]]
|
||||
if let text = text, let messages = messages, !text.isEmpty, messages.contains(item.message.index) {
|
||||
rectsSet = self.textNode.textRangesRects(text: text)
|
||||
rectsSet = self.textNode.textNode.textRangesRects(text: text)
|
||||
} else {
|
||||
rectsSet = []
|
||||
}
|
||||
@@ -754,9 +675,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
} else {
|
||||
textHighlightNode = LinkHighlightingNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.textHighlightColor : item.presentationData.theme.theme.chat.message.outgoing.textHighlightColor)
|
||||
self.textHighlightingNodes.append(textHighlightNode)
|
||||
self.insertSubnode(textHighlightNode, belowSubnode: self.textNode)
|
||||
self.insertSubnode(textHighlightNode, belowSubnode: self.textNode.textNode)
|
||||
}
|
||||
textHighlightNode.frame = self.textNode.frame
|
||||
textHighlightNode.frame = self.textNode.textNode.frame
|
||||
textHighlightNode.updateRects(rects)
|
||||
}
|
||||
for i in (rectsSet.count ..< self.textHighlightingNodes.count).reversed() {
|
||||
@@ -791,7 +712,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
knobColor = item.presentationData.theme.theme.chat.message.outgoing.textSelectionKnobColor
|
||||
}
|
||||
|
||||
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: selectionColor, knob: knobColor), strings: item.presentationData.strings, textNode: self.textNode, updateIsActive: { [weak self] value in
|
||||
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: selectionColor, knob: knobColor), strings: item.presentationData.strings, textNode: self.textNode.textNode, updateIsActive: { [weak self] value in
|
||||
self?.updateIsTextSelectionActive?(value)
|
||||
}, present: { [weak self] c, a in
|
||||
self?.item?.controllerInteraction.presentGlobalOverlayController(c, a)
|
||||
@@ -802,7 +723,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
item.controllerInteraction.performTextSelectionAction(item.message.stableId, text, action)
|
||||
})
|
||||
textSelectionNode.updateRange = { [weak self] selectionRange in
|
||||
if let strongSelf = self, let dustNode = strongSelf.dustNode, !dustNode.isRevealed, let textLayout = strongSelf.textNode.cachedLayout, !textLayout.spoilers.isEmpty, let selectionRange = selectionRange {
|
||||
if let strongSelf = self, let dustNode = strongSelf.dustNode, !dustNode.isRevealed, let textLayout = strongSelf.textNode.textNode.cachedLayout, !textLayout.spoilers.isEmpty, let selectionRange = selectionRange {
|
||||
for (spoilerRange, _) in textLayout.spoilers {
|
||||
if let intersection = selectionRange.intersection(spoilerRange), intersection.length > 0 {
|
||||
dustNode.update(revealed: true)
|
||||
@@ -813,9 +734,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
self.textSelectionNode = textSelectionNode
|
||||
self.addSubnode(textSelectionNode)
|
||||
self.insertSubnode(textSelectionNode.highlightAreaNode, belowSubnode: self.textNode)
|
||||
textSelectionNode.frame = self.textNode.frame
|
||||
textSelectionNode.highlightAreaNode.frame = self.textNode.frame
|
||||
self.insertSubnode(textSelectionNode.highlightAreaNode, belowSubnode: self.textNode.textNode)
|
||||
textSelectionNode.frame = self.textNode.textNode.frame
|
||||
textSelectionNode.highlightAreaNode.frame = self.textNode.textNode.frame
|
||||
}
|
||||
} else {
|
||||
if let textSelectionNode = self.textSelectionNode {
|
||||
@@ -851,14 +772,14 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
sourceView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak sourceView] _ in
|
||||
sourceView?.removeFromSuperview()
|
||||
})
|
||||
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.08)
|
||||
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.08)
|
||||
|
||||
let offset = CGPoint(
|
||||
x: sourceView.frame.minX - (self.textNode.frame.minX - 0.0),
|
||||
y: sourceView.frame.minY - (self.textNode.frame.minY - 3.0) - scrollOffset
|
||||
x: sourceView.frame.minX - (self.textNode.textNode.frame.minX - 0.0),
|
||||
y: sourceView.frame.minY - (self.textNode.textNode.frame.minY - 3.0) - scrollOffset
|
||||
)
|
||||
|
||||
transition.vertical.animatePositionAdditive(node: self.textNode, offset: offset)
|
||||
transition.vertical.animatePositionAdditive(node: self.textNode.textNode, offset: offset)
|
||||
transition.updatePosition(layer: sourceView.layer, position: CGPoint(x: sourceView.layer.position.x - offset.x, y: sourceView.layer.position.y - offset.y))
|
||||
|
||||
self.statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
|
||||
Reference in New Issue
Block a user