Reaction improvements

This commit is contained in:
Ali
2021-12-22 23:47:25 +04:00
parent e004d61da3
commit ccffdb6bac
3 changed files with 186 additions and 100 deletions

View File

@@ -393,16 +393,16 @@ public extension ContainedViewLayoutTransition {
} }
} }
func animatePositionWithKeyframes(node: ASDisplayNode, keyframes: [AnyObject], removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { func animatePositionWithKeyframes(node: ASDisplayNode, keyframes: [CGPoint], removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
self.animatePositionWithKeyframes(layer: node.layer, keyframes: keyframes, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion) self.animatePositionWithKeyframes(layer: node.layer, keyframes: keyframes, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
} }
func animatePositionWithKeyframes(layer: CALayer, keyframes: [AnyObject], removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { func animatePositionWithKeyframes(layer: CALayer, keyframes: [CGPoint], removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
switch self { switch self {
case .immediate: case .immediate:
completion?(true) completion?(true)
case let .animated(duration, curve): case let .animated(duration, curve):
layer.animateKeyframes(values: keyframes, duration: duration, keyPath: "position", timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: { value in layer.animateKeyframes(values: keyframes.map(NSValue.init(cgPoint:)), duration: duration, keyPath: "position", timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: { value in
completion?(value) completion?(value)
}) })
} }

View File

@@ -41,6 +41,7 @@ private let largeCircleSize: CGFloat = 16.0
private let smallCircleSize: CGFloat = 8.0 private let smallCircleSize: CGFloat = 8.0
public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
private let context: AccountContext
private let theme: PresentationTheme private let theme: PresentationTheme
private let items: [ReactionContextItem] private let items: [ReactionContextItem]
@@ -49,7 +50,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
private let contentContainer: ASDisplayNode private let contentContainer: ASDisplayNode
private let contentContainerMask: UIImageView private let contentContainerMask: UIImageView
private let scrollNode: ASScrollNode private let scrollNode: ASScrollNode
private var itemNodes: [ReactionNode] = [] private var visibleItemNodes: [Int: ReactionNode] = [:]
private var isExpanded: Bool = true private var isExpanded: Bool = true
private var highlightedReaction: ReactionContextItem.Reaction? private var highlightedReaction: ReactionContextItem.Reaction?
@@ -64,7 +65,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
private weak var animationTargetView: UIView? private weak var animationTargetView: UIView?
private var animationHideNode: Bool = false private var animationHideNode: Bool = false
private var didAnimateIn: Bool = false
public init(context: AccountContext, theme: PresentationTheme, items: [ReactionContextItem]) { public init(context: AccountContext, theme: PresentationTheme, items: [ReactionContextItem]) {
self.context = context
self.theme = theme self.theme = theme
self.items = items self.items = items
@@ -116,11 +120,6 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
self.scrollNode.view.delegate = self self.scrollNode.view.delegate = self
self.itemNodes = self.items.map { item in
return ReactionNode(context: context, theme: theme, item: item)
}
self.itemNodes.forEach(self.scrollNode.addSubnode)
self.addSubnode(self.contentContainer) self.addSubnode(self.contentContainer)
} }
@@ -180,30 +179,61 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
} }
private func updateScrolling(transition: ContainedViewLayoutTransition) { private func updateScrolling(transition: ContainedViewLayoutTransition) {
let sideInset: CGFloat = 14.0 let sideInset: CGFloat = 11.0
let minScale: CGFloat = 0.6 let itemSpacing: CGFloat = 9.0
let scaleDistance: CGFloat = 30.0 let itemSize: CGFloat = 40.0
let verticalInset: CGFloat = 13.0
let rowHeight: CGFloat = 30.0
let visibleBounds = self.scrollNode.view.bounds let visibleBounds = self.scrollNode.view.bounds
for itemNode in self.itemNodes { var validIndices = Set<Int>()
if itemNode.isExtracted { for i in 0 ..< self.items.count {
continue let columnIndex = i
} let column = CGFloat(columnIndex)
let itemScale: CGFloat
let itemFrame = itemNode.frame.offsetBy(dx: -visibleBounds.minX, dy: 0.0) let itemOffsetY: CGFloat = -1.0
if itemFrame.minX < sideInset || itemFrame.maxX > visibleBounds.width - sideInset {
let edgeDistance: CGFloat let itemFrame = CGRect(origin: CGPoint(x: sideInset + column * (itemSize + itemSpacing), y: verticalInset + floor((rowHeight - itemSize) / 2.0) + itemOffsetY), size: CGSize(width: itemSize, height: itemSize))
if itemFrame.minX < sideInset { /*if self.highlightedReaction == self.items[i].reaction {
edgeDistance = sideInset - itemFrame.minX itemFrame = itemFrame.insetBy(dx: -6.0, dy: -6.0)
}*/
if visibleBounds.intersects(itemFrame) {
validIndices.insert(i)
var animateIn = false
let itemNode: ReactionNode
if let current = self.visibleItemNodes[i] {
itemNode = current
} else { } else {
edgeDistance = itemFrame.maxX - (visibleBounds.width - sideInset) animateIn = self.didAnimateIn
itemNode = ReactionNode(context: self.context, theme: self.theme, item: self.items[i])
self.visibleItemNodes[i] = itemNode
self.scrollNode.addSubnode(itemNode)
}
if !itemNode.isExtracted {
transition.updateFrame(node: itemNode, frame: itemFrame, beginWithCurrentState: true)
itemNode.updateLayout(size: itemFrame.size, isExpanded: false, transition: transition)
if animateIn {
itemNode.animateIn()
}
} }
let edgeFactor: CGFloat = min(1.0, edgeDistance / scaleDistance)
itemScale = edgeFactor * minScale + (1.0 - edgeFactor) * 1.0
} else {
itemScale = 1.0
} }
transition.updateSublayerTransformScale(node: itemNode, scale: itemScale) }
var removedIndices: [Int] = []
for (index, itemNode) in self.visibleItemNodes {
if !validIndices.contains(index) {
removedIndices.append(index)
itemNode.removeFromSupernode()
}
}
for index in removedIndices {
self.visibleItemNodes.removeValue(forKey: index)
} }
} }
@@ -237,22 +267,6 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
self.scrollNode.view.contentSize = CGSize(width: completeContentWidth, height: backgroundFrame.size.height) self.scrollNode.view.contentSize = CGSize(width: completeContentWidth, height: backgroundFrame.size.height)
for i in 0 ..< self.items.count {
let columnIndex = i
let column = CGFloat(columnIndex)
let itemOffsetY: CGFloat = -1.0
var itemFrame = CGRect(origin: CGPoint(x: sideInset + column * (itemSize + itemSpacing), y: verticalInset + floor((rowHeight - itemSize) / 2.0) + itemOffsetY), size: CGSize(width: itemSize, height: itemSize))
if self.highlightedReaction == self.items[i].reaction {
itemFrame = itemFrame.insetBy(dx: -6.0, dy: -6.0)
}
if !self.itemNodes[i].isExtracted {
transition.updateFrame(node: self.itemNodes[i], frame: itemFrame, beginWithCurrentState: true)
self.itemNodes[i].updateLayout(size: itemFrame.size, isExpanded: false, transition: transition)
}
}
self.updateScrolling(transition: transition) self.updateScrolling(transition: transition)
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
@@ -291,23 +305,28 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil) self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil)
} }
let mainCircleDuration: Double = 0.5 //let mainCircleDuration: Double = 0.5
let mainCircleDelay: Double = 0.1 let mainCircleDelay: Double = 0.1
self.backgroundNode.animateIn() self.backgroundNode.animateIn()
for i in 0 ..< self.itemNodes.count { self.didAnimateIn = true
let itemNode = self.itemNodes[i]
for i in 0 ..< self.items.count {
guard let itemNode = self.visibleItemNodes[i] else {
continue
}
let itemDelay = mainCircleDelay + 0.1 + Double(i) * 0.03 let itemDelay = mainCircleDelay + 0.1 + Double(i) * 0.03
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: itemDelay) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + itemDelay, execute: { [weak itemNode] in
itemNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: itemDelay, initialVelocity: 0.0) itemNode?.animateIn()
})
} }
} }
public func animateOut(to targetAnchorRect: CGRect?, animatingOutToReaction: Bool) { public func animateOut(to targetAnchorRect: CGRect?, animatingOutToReaction: Bool) {
self.backgroundNode.animateOut() self.backgroundNode.animateOut()
for itemNode in self.itemNodes { for (_, itemNode) in self.visibleItemNodes {
if itemNode.isExtracted { if itemNode.isExtracted {
continue continue
} }
@@ -367,7 +386,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
} }
public func willAnimateOutToReaction(value: String) { public func willAnimateOutToReaction(value: String) {
for itemNode in self.itemNodes { for (_, itemNode) in self.visibleItemNodes {
if itemNode.item.reaction.rawValue != value { if itemNode.item.reaction.rawValue != value {
continue continue
} }
@@ -376,7 +395,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
} }
public func animateOutToReaction(value: String, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) { public func animateOutToReaction(value: String, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) {
for itemNode in self.itemNodes { for (_, itemNode) in self.visibleItemNodes {
if itemNode.item.reaction.rawValue != value { if itemNode.item.reaction.rawValue != value {
continue continue
} }
@@ -400,7 +419,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let selfSourceRect = itemNode.view.convert(itemNode.view.bounds, to: self.view) let selfSourceRect = itemNode.view.convert(itemNode.view.bounds, to: self.view)
let selfTargetRect = self.view.convert(targetView.bounds, from: targetView) let selfTargetRect = self.view.convert(targetView.bounds, from: targetView)
let expandedScale: CGFloat = 3.0 let expandedScale: CGFloat = 4.0
let expandedSize = CGSize(width: floor(selfSourceRect.width * expandedScale), height: floor(selfSourceRect.height * expandedScale)) let expandedSize = CGSize(width: floor(selfSourceRect.width * expandedScale), height: floor(selfSourceRect.height * expandedScale))
var expandedFrame = CGRect(origin: CGPoint(x: floor(selfTargetRect.midX - expandedSize.width / 2.0), y: floor(selfTargetRect.midY - expandedSize.height / 2.0)), size: expandedSize) var expandedFrame = CGRect(origin: CGPoint(x: floor(selfTargetRect.midX - expandedSize.width / 2.0), y: floor(selfTargetRect.midY - expandedSize.height / 2.0)), size: expandedSize)
@@ -411,10 +430,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .linear) let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .linear)
self.addSubnode(itemNode) self.addSubnode(itemNode)
itemNode.frame = selfSourceRect //itemNode.position = selfSourceRect.center
itemNode.position = expandedFrame.center itemNode.position = expandedFrame.center
transition.updateBounds(node: itemNode, bounds: CGRect(origin: CGPoint(), size: expandedFrame.size)) transition.updateBounds(node: itemNode, bounds: CGRect(origin: CGPoint(), size: expandedFrame.size))
itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, transition: transition) itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, transition: transition)
transition.animatePositionWithKeyframes(node: itemNode, keyframes: generateParabollicMotionKeyframes(from: selfSourceRect.center, to: expandedFrame.center, elevation: 30.0)) transition.animatePositionWithKeyframes(node: itemNode, keyframes: generateParabollicMotionKeyframes(from: selfSourceRect.center, to: expandedFrame.center, elevation: 30.0))
let additionalAnimationNode = AnimatedStickerNode() let additionalAnimationNode = AnimatedStickerNode()
@@ -507,7 +527,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
public func reaction(at point: CGPoint) -> ReactionContextItem? { public func reaction(at point: CGPoint) -> ReactionContextItem? {
for i in 0 ..< 2 { for i in 0 ..< 2 {
let touchInset: CGFloat = i == 0 ? 0.0 : 8.0 let touchInset: CGFloat = i == 0 ? 0.0 : 8.0
for itemNode in self.itemNodes { for (_, itemNode) in self.visibleItemNodes {
let itemPoint = self.view.convert(point, to: itemNode.view) let itemPoint = self.view.convert(point, to: itemNode.view)
if itemNode.bounds.insetBy(dx: -touchInset, dy: -touchInset).contains(itemPoint) { if itemNode.bounds.insetBy(dx: -touchInset, dy: -touchInset).contains(itemPoint) {
return itemNode.item return itemNode.item
@@ -518,7 +538,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
} }
public func performReactionSelection(reaction: ReactionContextItem.Reaction) { public func performReactionSelection(reaction: ReactionContextItem.Reaction) {
for itemNode in self.itemNodes { for (_, itemNode) in self.visibleItemNodes {
if itemNode.item.reaction == reaction { if itemNode.item.reaction == reaction {
self.reactionSelected?(itemNode.item) self.reactionSelected?(itemNode.item)
break break
@@ -782,7 +802,7 @@ public final class StandaloneDismissReactionAnimation: ASDisplayNode {
} }
} }
private func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to targetPosition: CGPoint, elevation: CGFloat) -> [AnyObject] { private func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to targetPosition: CGPoint, elevation: CGFloat) -> [CGPoint] {
let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y - elevation) let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y - elevation)
let x1 = sourcePoint.x let x1 = sourcePoint.x
@@ -792,13 +812,13 @@ private func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to tar
let x3 = targetPosition.x let x3 = targetPosition.x
let y3 = targetPosition.y let y3 = targetPosition.y
var keyframes: [AnyObject] = [] var keyframes: [CGPoint] = []
if abs(y1 - y3) < 5.0 && abs(x1 - x3) < 5.0 { if abs(y1 - y3) < 5.0 && abs(x1 - x3) < 5.0 {
for i in 0 ..< 10 { for i in 0 ..< 10 {
let k = CGFloat(i) / CGFloat(10 - 1) let k = CGFloat(i) / CGFloat(10 - 1)
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
let y = sourcePoint.y * (1.0 - k) + targetPosition.y * k let y = sourcePoint.y * (1.0 - k) + targetPosition.y * k
keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y))) keyframes.append(CGPoint(x: x, y: y))
} }
} else { } else {
let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3)) let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
@@ -809,7 +829,7 @@ private func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to tar
let k = CGFloat(i) / CGFloat(10 - 1) let k = CGFloat(i) / CGFloat(10 - 1)
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
let y = a * x * x + b * x + c let y = a * x * x + b * x + c
keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y))) keyframes.append(CGPoint(x: x, y: y))
} }
} }

View File

@@ -38,8 +38,10 @@ private let font = Font.medium(13.0)
final class ReactionNode: ASDisplayNode { final class ReactionNode: ASDisplayNode {
let context: AccountContext let context: AccountContext
let item: ReactionContextItem let item: ReactionContextItem
private let staticImageNode: TransformImageNode
private let stillAnimationNode: AnimatedStickerNode private var animateInAnimationNode: AnimatedStickerNode?
private let staticAnimationNode: AnimatedStickerNode
private var stillAnimationNode: AnimatedStickerNode?
private var animationNode: AnimatedStickerNode? private var animationNode: AnimatedStickerNode?
private var fetchStickerDisposable: Disposable? private var fetchStickerDisposable: Disposable?
@@ -55,22 +57,40 @@ final class ReactionNode: ASDisplayNode {
self.context = context self.context = context
self.item = item self.item = item
self.staticImageNode = TransformImageNode() self.staticAnimationNode = AnimatedStickerNode()
self.staticAnimationNode.isHidden = true
self.animateInAnimationNode = AnimatedStickerNode()
self.stillAnimationNode = AnimatedStickerNode() self.stillAnimationNode = AnimatedStickerNode()
super.init() super.init()
self.addSubnode(self.staticImageNode) if let animateInAnimationNode = self.animateInAnimationNode {
self.addSubnode(animateInAnimationNode)
}
self.addSubnode(self.staticAnimationNode)
self.addSubnode(self.stillAnimationNode) self.animateInAnimationNode?.completed = { [weak self] _ in
self.stillAnimationNode.started = { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.staticImageNode.isHidden = true if strongSelf.animationNode == nil {
strongSelf.staticAnimationNode.isHidden = false
}
strongSelf.animateInAnimationNode?.removeFromSupernode()
strongSelf.animateInAnimationNode = nil
} }
/*self.stillAnimationNode.started = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.animateInAnimationNode.isHidden = true
strongSelf.animateInAnimationNode.visibility = false
}*/
self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.appearAnimation.resource)).start()
self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.stillAnimation.resource)).start() self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.stillAnimation.resource)).start()
self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.listAnimation.resource)).start() self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.listAnimation.resource)).start()
self.fetchFullAnimationDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.applicationAnimation.resource)).start() self.fetchFullAnimationDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.applicationAnimation.resource)).start()
@@ -81,6 +101,10 @@ final class ReactionNode: ASDisplayNode {
self.fetchFullAnimationDisposable?.dispose() self.fetchFullAnimationDisposable?.dispose()
} }
func animateIn() {
self.animateInAnimationNode?.visibility = true
}
func updateLayout(size: CGSize, isExpanded: Bool, transition: ContainedViewLayoutTransition) { func updateLayout(size: CGSize, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
let intrinsicSize = size let intrinsicSize = size
@@ -101,60 +125,102 @@ final class ReactionNode: ASDisplayNode {
self.animationNode = animationNode self.animationNode = animationNode
self.addSubnode(animationNode) self.addSubnode(animationNode)
animationNode.started = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.staticImageNode.isHidden = true
}
animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.listAnimation.resource), width: Int(animationDisplaySize.width * 2.0), height: Int(animationDisplaySize.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.listAnimation.resource.id))) animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.listAnimation.resource), width: Int(animationDisplaySize.width * 2.0), height: Int(animationDisplaySize.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.listAnimation.resource.id)))
animationNode.frame = animationFrame animationNode.frame = animationFrame
animationNode.updateLayout(size: animationFrame.size) animationNode.updateLayout(size: animationFrame.size)
if transition.isAnimated, !self.staticImageNode.frame.isEmpty {
transition.animateTransformScale(node: animationNode, from: self.staticImageNode.bounds.width / animationFrame.width)
transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: self.staticImageNode.frame.midX - animationFrame.midX, y: self.staticImageNode.frame.midY - animationFrame.midY))
}
animationNode.visibility = true
self.stillAnimationNode.alpha = 0.0
if transition.isAnimated { if transition.isAnimated {
self.stillAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in if let stillAnimationNode = self.stillAnimationNode, !stillAnimationNode.frame.isEmpty {
self?.stillAnimationNode.visibility = false stillAnimationNode.alpha = 0.0
}) stillAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in
guard let strongSelf = self, let stillAnimationNode = strongSelf.stillAnimationNode else {
return
}
strongSelf.stillAnimationNode = nil
stillAnimationNode.removeFromSupernode()
})
}
if let animateInAnimationNode = self.animateInAnimationNode {
animateInAnimationNode.alpha = 0.0
animateInAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in
guard let strongSelf = self, let animateInAnimationNode = strongSelf.animateInAnimationNode else {
return
}
strongSelf.animateInAnimationNode = nil
animateInAnimationNode.removeFromSupernode()
})
}
var referenceNode: ASDisplayNode?
if let animateInAnimationNode = self.animateInAnimationNode {
referenceNode = animateInAnimationNode
} else if !self.staticAnimationNode.isHidden {
referenceNode = self.staticAnimationNode
}
if let referenceNode = referenceNode {
transition.animateTransformScale(node: animationNode, from: referenceNode.bounds.width / animationFrame.width)
transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: referenceNode.frame.midX - animationFrame.midX, y: referenceNode.frame.midY - animationFrame.midY))
}
if !self.staticAnimationNode.isHidden {
transition.animateTransformScale(node: self.staticAnimationNode, from: self.staticAnimationNode.bounds.width / animationFrame.width)
transition.animatePositionAdditive(node: self.staticAnimationNode, offset: CGPoint(x: self.staticAnimationNode.frame.midX - animationFrame.midX, y: self.staticAnimationNode.frame.midY - animationFrame.midY))
self.staticAnimationNode.alpha = 0.0
self.staticAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
}
animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
} else { } else {
self.stillAnimationNode.visibility = false if let stillAnimationNode = self.stillAnimationNode {
self.stillAnimationNode = nil
stillAnimationNode.removeFromSupernode()
}
self.staticAnimationNode.isHidden = true
} }
animationNode.visibility = true
} }
if self.validSize != size { if self.validSize != size {
self.validSize = size self.validSize = size
if !self.staticImageNode.isHidden {
self.staticImageNode.setSignal(chatMessageAnimatedSticker(postbox: self.context.account.postbox, file: item.stillAnimation, small: false, size: CGSize(width: animationDisplaySize.width * UIScreenScale, height: animationDisplaySize.height * UIScreenScale), fitzModifier: nil, fetched: false, onlyFullSize: false, thumbnail: false, synchronousLoad: false))
let imageApply = self.staticImageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: animationDisplaySize, boundingSize: animationDisplaySize, intrinsicInsets: UIEdgeInsets()))
imageApply()
}
transition.updateFrame(node: self.staticImageNode, frame: animationFrame)
} }
if !self.didSetupStillAnimation { if !self.didSetupStillAnimation {
if self.animationNode == nil { if self.animationNode == nil {
self.didSetupStillAnimation = true self.didSetupStillAnimation = true
self.stillAnimationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.stillAnimation.resource), width: Int(animationDisplaySize.width * 2.5), height: Int(animationDisplaySize.height * 2.5), playbackMode: .loop, mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.stillAnimation.resource.id))) self.staticAnimationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.stillAnimation.resource), width: Int(animationDisplaySize.width * 2.5), height: Int(animationDisplaySize.height * 2.5), playbackMode: .still(.start), mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.stillAnimation.resource.id)))
self.staticAnimationNode.position = animationFrame.center
self.staticAnimationNode.bounds = CGRect(origin: CGPoint(), size: animationFrame.size)
self.staticAnimationNode.updateLayout(size: animationFrame.size)
self.staticAnimationNode.visibility = true
if let animateInAnimationNode = self.animateInAnimationNode {
animateInAnimationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.appearAnimation.resource), width: Int(animationDisplaySize.width * 2.5), height: Int(animationDisplaySize.height * 2.5), playbackMode: .once, mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.appearAnimation.resource.id)))
animateInAnimationNode.position = animationFrame.center
animateInAnimationNode.bounds = CGRect(origin: CGPoint(), size: animationFrame.size)
animateInAnimationNode.updateLayout(size: animationFrame.size)
}
/*self.stillAnimationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.stillAnimation.resource), width: Int(animationDisplaySize.width * 2.5), height: Int(animationDisplaySize.height * 2.5), playbackMode: .once, mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.stillAnimation.resource.id)))
self.stillAnimationNode.position = animationFrame.center self.stillAnimationNode.position = animationFrame.center
self.stillAnimationNode.bounds = CGRect(origin: CGPoint(), size: animationFrame.size) self.stillAnimationNode.bounds = CGRect(origin: CGPoint(), size: animationFrame.size)
self.stillAnimationNode.updateLayout(size: animationFrame.size) self.stillAnimationNode.updateLayout(size: animationFrame.size)*/
self.stillAnimationNode.visibility = true
} }
} else { } else {
transition.updatePosition(node: self.stillAnimationNode, position: animationFrame.center, beginWithCurrentState: true) transition.updatePosition(node: self.staticAnimationNode, position: animationFrame.center, beginWithCurrentState: true)
transition.updateTransformScale(node: self.stillAnimationNode, scale: animationFrame.size.width / self.stillAnimationNode.bounds.width, beginWithCurrentState: true) transition.updateTransformScale(node: self.staticAnimationNode, scale: animationFrame.size.width / self.staticAnimationNode.bounds.width, beginWithCurrentState: true)
if let animateInAnimationNode = self.animateInAnimationNode {
transition.updatePosition(node: animateInAnimationNode, position: animationFrame.center, beginWithCurrentState: true)
transition.updateTransformScale(node: animateInAnimationNode, scale: animationFrame.size.width / animateInAnimationNode.bounds.width, beginWithCurrentState: true)
}
if let stillAnimationNode = self.stillAnimationNode {
transition.updatePosition(node: stillAnimationNode, position: animationFrame.center, beginWithCurrentState: true)
transition.updateTransformScale(node: stillAnimationNode, scale: animationFrame.size.width / stillAnimationNode.bounds.width, beginWithCurrentState: true)
}
} }
} }
func didAppear() {
}
} }