Forum sharing fixes

This commit is contained in:
Ilya Laktyushin 2022-10-20 05:29:11 +03:00
parent f4ceb40d22
commit 02e53fd67c
5 changed files with 127 additions and 72 deletions

View File

@ -73,7 +73,7 @@ public final class SelectablePeerNode: ASDisplayNode {
private let avatarNode: AvatarNode private let avatarNode: AvatarNode
private let onlineNode: PeerOnlineMarkerNode private let onlineNode: PeerOnlineMarkerNode
private var checkNode: CheckNode? private var checkNode: CheckNode?
private let textNode: ASTextNode private let textNode: ImmediateTextNode
private let iconView: ComponentView<Empty> private let iconView: ComponentView<Empty>
@ -117,7 +117,7 @@ public final class SelectablePeerNode: ASDisplayNode {
self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 60.0, height: 60.0)) self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 60.0, height: 60.0))
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled() self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
self.textNode = ASTextNode() self.textNode = ImmediateTextNode()
self.textNode.isUserInteractionEnabled = false self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = false self.textNode.displaysAsynchronously = false
@ -309,16 +309,16 @@ public final class SelectablePeerNode: ASDisplayNode {
self.avatarNodeContainer.frame = CGRect(origin: CGPoint(x: floor((bounds.size.width - 60.0) / 2.0), y: 4.0), size: CGSize(width: 60.0, height: 60.0)) self.avatarNodeContainer.frame = CGRect(origin: CGPoint(x: floor((bounds.size.width - 60.0) / 2.0), y: 4.0), size: CGSize(width: 60.0, height: 60.0))
let iconSize = CGSize(width: 18.0, height: 18.0) let iconSize = CGSize(width: 18.0, height: 18.0)
let textSize = self.textNode.calculateSizeThatFits(bounds.size) let textSize = self.textNode.updateLayout(bounds.size)
var totalWidth = textSize.width var totalWidth = textSize.width
var leftOrigin = floorToScreenPixels((bounds.width - textSize.width) / 2.0) var leftOrigin = floorToScreenPixels((bounds.width - textSize.width) / 2.0)
if let iconView = self.iconView.view, iconView.superview != nil { if let iconView = self.iconView.view, iconView.superview != nil {
totalWidth += iconView.frame.width + 2.0 totalWidth += iconView.frame.width + 2.0
leftOrigin = floorToScreenPixels((bounds.width - totalWidth) / 2.0) leftOrigin = floorToScreenPixels((bounds.width - totalWidth) / 2.0)
iconView.frame = CGRect(origin: CGPoint(x: leftOrigin, y: 4.0 + 60.0 + 1.0), size: iconSize) iconView.frame = CGRect(origin: CGPoint(x: leftOrigin, y: 4.0 + 60.0 + 4.0 + floorToScreenPixels((textSize.height - iconSize.height) / 2.0)), size: iconSize)
leftOrigin += iconSize.width + 2.0 leftOrigin += iconSize.width + 2.0
} }
self.textNode.frame = CGRect(origin: CGPoint(x: leftOrigin, y: 4.0 + 60.0 + 4.0), size: CGSize(width: textSize.width, height: 34.0)) self.textNode.frame = CGRect(origin: CGPoint(x: leftOrigin, y: 4.0 + 60.0 + 4.0), size: textSize)
let avatarFrame = self.avatarNode.frame let avatarFrame = self.avatarNode.frame
let avatarContainerFrame = self.avatarNodeContainer.frame let avatarContainerFrame = self.avatarNodeContainer.frame

View File

@ -392,34 +392,47 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
strongSelf.topicsContentNode = topicsContentNode strongSelf.topicsContentNode = topicsContentNode
strongSelf.contentNode?.supernode?.addSubnode(topicsContentNode) strongSelf.contentNode?.supernode?.addSubnode(topicsContentNode)
topicsContentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
})
if let (layout, navigationBarHeight, _) = strongSelf.containerLayout { if let (layout, navigationBarHeight, _) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
} }
if let sourceFrame = strongSelf.peersContentNode?.animateOut(peerId: peer.peerId) { if let peersContentNode = strongSelf.peersContentNode {
topicsContentNode.animateIn(sourceFrame: sourceFrame) peersContentNode.setContentOffsetUpdated(nil)
let scrollDelta = topicsContentNode.contentGridNode.scrollView.contentOffset.y - peersContentNode.contentGridNode.scrollView.contentOffset.y
if let sourceFrame = peersContentNode.animateOut(peerId: peer.peerId, scrollDelta: scrollDelta) {
topicsContentNode.animateIn(sourceFrame: sourceFrame, scrollDelta: scrollDelta)
} }
}
topicsContentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
})
strongSelf.contentNodeOffsetUpdated(topicsContentNode.contentGridNode.scrollView.contentOffset.y, transition: .animated(duration: 0.4, curve: .spring))
}) })
} }
func closePeerTopics(_ peerId: EnginePeer.Id) { func closePeerTopics(_ peerId: EnginePeer.Id) {
if let topicsContentNode = self.topicsContentNode, let peersContentNode = self.peersContentNode { if let topicsContentNode = self.topicsContentNode, let peersContentNode = self.peersContentNode {
topicsContentNode.setContentOffsetUpdated(nil)
topicsContentNode.supernode?.insertSubnode(topicsContentNode, belowSubnode: peersContentNode) topicsContentNode.supernode?.insertSubnode(topicsContentNode, belowSubnode: peersContentNode)
} }
self.peersContentNode?.setContentOffsetUpdated({ [weak self] contentOffset, transition in
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
})
if let (layout, navigationBarHeight, _) = self.containerLayout { if let (layout, navigationBarHeight, _) = self.containerLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
} }
if let targetFrame = self.peersContentNode?.animateIn(peerId: peerId), let topicsContentNode = self.topicsContentNode { if let peersContentNode = self.peersContentNode {
topicsContentNode.animateOut(targetFrame: targetFrame, completion: { [weak self] in peersContentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
})
self.contentNodeOffsetUpdated(peersContentNode.contentGridNode.scrollView.contentOffset.y, transition: .animated(duration: 0.4, curve: .spring))
}
if let peersContentNode = self.peersContentNode, let topicsContentNode = self.topicsContentNode {
let scrollDelta = topicsContentNode.contentGridNode.scrollView.contentOffset.y - peersContentNode.contentGridNode.scrollView.contentOffset.y
if let targetFrame = peersContentNode.animateIn(peerId: peerId, scrollDelta: scrollDelta) {
topicsContentNode.animateOut(targetFrame: targetFrame, scrollDelta: scrollDelta, completion: { [weak self] in
if let topicsContentNode = self?.topicsContentNode { if let topicsContentNode = self?.topicsContentNode {
topicsContentNode.removeFromSupernode() topicsContentNode.removeFromSupernode()
self?.topicsContentNode = nil self?.topicsContentNode = nil
@ -427,6 +440,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
}) })
} }
} }
}
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
guard self.presentationData !== presentationData else { guard self.presentationData !== presentationData else {

View File

@ -115,7 +115,8 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
private var entries: [SharePeerEntry] = [] private var entries: [SharePeerEntry] = []
private var enqueuedTransitions: [(ShareGridTransaction, Bool)] = [] private var enqueuedTransitions: [(ShareGridTransaction, Bool)] = []
private let contentGridNode: GridNode let contentGridNode: GridNode
private let headerNode: ASDisplayNode
private let contentTitleNode: ASTextNode private let contentTitleNode: ASTextNode
private let contentSubtitleNode: ASTextNode private let contentSubtitleNode: ASTextNode
private let contentTitleAccountNode: AvatarNode private let contentTitleAccountNode: AvatarNode
@ -193,6 +194,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
} }
self.contentGridNode = GridNode() self.contentGridNode = GridNode()
self.headerNode = ASDisplayNode()
self.contentTitleNode = ASTextNode() self.contentTitleNode = ASTextNode()
self.contentTitleNode.attributedText = NSAttributedString(string: strings.ShareMenu_ShareTo, font: Font.medium(20.0), textColor: self.theme.actionSheet.primaryTextColor) self.contentTitleNode.attributedText = NSAttributedString(string: strings.ShareMenu_ShareTo, font: Font.medium(20.0), textColor: self.theme.actionSheet.primaryTextColor)
@ -247,17 +249,19 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
super.init() super.init()
self.addSubnode(self.contentGridNode) self.addSubnode(self.contentGridNode)
self.addSubnode(self.headerNode)
self.addSubnode(self.contentTitleNode) self.headerNode.addSubnode(self.contentTitleNode)
self.addSubnode(self.contentSubtitleNode) self.headerNode.addSubnode(self.contentSubtitleNode)
self.addSubnode(self.contentTitleAccountNode) self.headerNode.addSubnode(self.contentTitleAccountNode)
self.addSubnode(self.segmentedNode) self.headerNode.addSubnode(self.segmentedNode)
self.addSubnode(self.searchButtonNode) self.headerNode.addSubnode(self.searchButtonNode)
self.shareContainerNode.addSubnode(self.shareReferenceNode) self.shareContainerNode.addSubnode(self.shareReferenceNode)
self.shareButtonNode.addSubnode(self.shareContainerNode) self.shareButtonNode.addSubnode(self.shareContainerNode)
self.addSubnode(self.shareButtonNode) self.headerNode.addSubnode(self.shareButtonNode)
self.addSubnode(self.contentSeparatorNode) self.addSubnode(self.contentSeparatorNode)
self.shareContainerNode.activated = { [weak self] gesture, _ in self.shareContainerNode.activated = { [weak self] gesture, _ in
@ -367,7 +371,11 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
node = itemNode node = itemNode
} }
} }
return node?.frame.offsetBy(dx: 0.0, dy: -10.0) if let node = node {
return node.frame.offsetBy(dx: 0.0, dy: -10.0)
} else {
return nil
}
} }
func generateMaskImage() -> UIImage? { func generateMaskImage() -> UIImage? {
@ -385,11 +393,17 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
})?.stretchableImage(withLeftCapWidth: 49, topCapHeight: 49) })?.stretchableImage(withLeftCapWidth: 49, topCapHeight: 49)
} }
func animateIn(peerId: EnginePeer.Id) -> CGRect? { func animateIn(peerId: EnginePeer.Id, scrollDelta: CGFloat) -> CGRect? {
self.headerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -scrollDelta), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.searchButtonNode.alpha = 1.0 self.searchButtonNode.alpha = 1.0
self.searchButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.searchButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.searchButtonNode.layer.animatePosition(from: CGPoint(x: -20.0, y: 0.0), to: .zero, duration: 0.2, additive: true) self.searchButtonNode.layer.animatePosition(from: CGPoint(x: -20.0, y: 0.0), to: .zero, duration: 0.2, additive: true)
self.shareButtonNode.alpha = 1.0
self.shareButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.shareButtonNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 0.0), to: .zero, duration: 0.2, additive: true)
self.contentTitleNode.alpha = 1.0 self.contentTitleNode.alpha = 1.0
self.contentTitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.contentTitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.contentTitleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -10.0), to: .zero, duration: 0.2, additive: true) self.contentTitleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -10.0), to: .zero, duration: 0.2, additive: true)
@ -400,14 +414,17 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.contentSubtitleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -10.0), to: .zero, duration: 0.2, additive: true) self.contentSubtitleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -10.0), to: .zero, duration: 0.2, additive: true)
self.contentSubtitleNode.layer.animateScale(from: 0.85, to: 1.0, duration: 0.2) self.contentSubtitleNode.layer.animateScale(from: 0.85, to: 1.0, duration: 0.2)
self.contentGridNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -scrollDelta), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
if let targetFrame = self.frameForPeerId(peerId), let (size, bottomInset) = self.validLayout { if let targetFrame = self.frameForPeerId(peerId), let (size, bottomInset) = self.validLayout {
let sourceCenter = targetFrame.center
let clippedNode = ASDisplayNode() let clippedNode = ASDisplayNode()
clippedNode.clipsToBounds = true clippedNode.clipsToBounds = true
clippedNode.cornerRadius = 16.0 clippedNode.cornerRadius = 16.0
clippedNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.contentTitleNode.frame.minY - 15.0), size: CGSize(width: size.width, height: size.height - bottomInset)) clippedNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.contentTitleNode.frame.minY - 15.0), size: CGSize(width: size.width, height: size.height - bottomInset))
self.contentGridNode.view.superview?.insertSubview(clippedNode.view, aboveSubview: self.contentGridNode.view) self.contentGridNode.view.superview?.insertSubview(clippedNode.view, aboveSubview: self.contentGridNode.view)
clippedNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -scrollDelta), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
let maskView = UIView() let maskView = UIView()
maskView.frame = clippedNode.bounds maskView.frame = clippedNode.bounds
@ -415,25 +432,24 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
maskImageView.image = generateMaskImage() maskImageView.image = generateMaskImage()
maskImageView.frame = maskView.bounds.offsetBy(dx: 0.0, dy: 36.0) maskImageView.frame = maskView.bounds.offsetBy(dx: 0.0, dy: 36.0)
maskView.addSubview(maskImageView) maskView.addSubview(maskImageView)
clippedNode.view.mask = maskView clippedNode.view.mask = maskView
self.contentGridNode.alpha = 1.0 self.contentGridNode.alpha = 1.0
self.contentGridNode.forEachItemNode { itemNode in self.contentGridNode.forEachItemNode { itemNode in
if let snapshotView = itemNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = itemNode.view.convert(itemNode.bounds, to: clippedNode.view)
if let itemNode = itemNode as? ShareControllerPeerGridItemNode, itemNode.peerId == peerId { if let itemNode = itemNode as? ShareControllerPeerGridItemNode, itemNode.peerId == peerId {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false) itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false)
itemNode.layer.animateScale(from: 1.35, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak clippedNode] _ in itemNode.layer.animateScale(from: 1.35, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak clippedNode] _ in
clippedNode?.view.removeFromSuperview() clippedNode?.view.removeFromSuperview()
}) })
} else { } else if let snapshotView = itemNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = itemNode.view.convert(itemNode.bounds, to: clippedNode.view)
clippedNode.view.addSubview(snapshotView) clippedNode.view.addSubview(snapshotView)
itemNode.alpha = 0.0 itemNode.alpha = 0.0
let angle = sourceCenter.angle(to: itemNode.position) let angle = targetFrame.center.angle(to: itemNode.position)
let distance = sourceCenter.distance(to: itemNode.position) let distance = targetFrame.center.distance(to: itemNode.position)
let newDistance = distance * 2.8 let newDistance = distance * 2.8
let newPosition = snapshotView.center.offsetBy(distance: newDistance, inDirection: angle) let newPosition = snapshotView.center.offsetBy(distance: newDistance, inDirection: angle)
snapshotView.layer.animatePosition(from: newPosition, to: snapshotView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) snapshotView.layer.animatePosition(from: newPosition, to: snapshotView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
@ -443,7 +459,6 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false) snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false)
} }
} }
}
return targetFrame return targetFrame
} else { } else {
@ -451,11 +466,17 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
} }
} }
func animateOut(peerId: EnginePeer.Id) -> CGRect? { func animateOut(peerId: EnginePeer.Id, scrollDelta: CGFloat) -> CGRect? {
self.headerNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -scrollDelta), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.searchButtonNode.alpha = 0.0 self.searchButtonNode.alpha = 0.0
self.searchButtonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) self.searchButtonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.searchButtonNode.layer.animatePosition(from: .zero, to: CGPoint(x: -20.0, y: 0.0), duration: 0.2, additive: true) self.searchButtonNode.layer.animatePosition(from: .zero, to: CGPoint(x: -20.0, y: 0.0), duration: 0.2, additive: true)
self.shareButtonNode.alpha = 0.0
self.shareButtonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.shareButtonNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 0.0), duration: 0.2, additive: true)
self.contentTitleNode.alpha = 0.0 self.contentTitleNode.alpha = 0.0
self.contentTitleNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) self.contentTitleNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.contentTitleNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, additive: true) self.contentTitleNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, additive: true)
@ -466,14 +487,17 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.contentSubtitleNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, additive: true) self.contentSubtitleNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, additive: true)
self.contentSubtitleNode.layer.animateScale(from: 1.0, to: 0.85, duration: 0.3) self.contentSubtitleNode.layer.animateScale(from: 1.0, to: 0.85, duration: 0.3)
self.contentGridNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -scrollDelta), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
if let sourceFrame = self.frameForPeerId(peerId), let (size, bottomInset) = self.validLayout { if let sourceFrame = self.frameForPeerId(peerId), let (size, bottomInset) = self.validLayout {
let sourceCenter = sourceFrame.center
let clippedNode = ASDisplayNode() let clippedNode = ASDisplayNode()
clippedNode.clipsToBounds = true clippedNode.clipsToBounds = true
clippedNode.cornerRadius = 16.0 clippedNode.cornerRadius = 16.0
clippedNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.contentTitleNode.frame.minY - 15.0), size: CGSize(width: size.width, height: size.height - bottomInset)) clippedNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.contentTitleNode.frame.minY - 15.0), size: CGSize(width: size.width, height: size.height - bottomInset))
self.contentGridNode.view.superview?.insertSubview(clippedNode.view, aboveSubview: self.contentGridNode.view) self.contentGridNode.view.superview?.insertSubview(clippedNode.view, aboveSubview: self.contentGridNode.view)
clippedNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -scrollDelta), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
let maskView = UIView() let maskView = UIView()
maskView.frame = clippedNode.bounds maskView.frame = clippedNode.bounds
@ -481,7 +505,6 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
maskImageView.image = generateMaskImage() maskImageView.image = generateMaskImage()
maskImageView.frame = maskView.bounds.offsetBy(dx: 0.0, dy: 36.0) maskImageView.frame = maskView.bounds.offsetBy(dx: 0.0, dy: 36.0)
maskView.addSubview(maskImageView) maskView.addSubview(maskImageView)
clippedNode.view.mask = maskView clippedNode.view.mask = maskView
self.contentGridNode.forEachItemNode { itemNode in self.contentGridNode.forEachItemNode { itemNode in
@ -492,8 +515,8 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
if let itemNode = itemNode as? ShareControllerPeerGridItemNode, itemNode.peerId == peerId { if let itemNode = itemNode as? ShareControllerPeerGridItemNode, itemNode.peerId == peerId {
} else { } else {
let angle = sourceCenter.angle(to: itemNode.position) let angle = sourceFrame.center.angle(to: itemNode.position)
let distance = sourceCenter.distance(to: itemNode.position) let distance = sourceFrame.center.distance(to: itemNode.position)
let newDistance = distance * 2.8 let newDistance = distance * 2.8
let newPosition = snapshotView.center.offsetBy(distance: newDistance, inDirection: angle) let newPosition = snapshotView.center.offsetBy(distance: newDistance, inDirection: angle)
snapshotView.layer.animatePosition(from: snapshotView.center, to: newPosition, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) snapshotView.layer.animatePosition(from: snapshotView.center, to: newPosition, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
@ -562,12 +585,15 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
let rawTitleOffset = -titleAreaHeight - presentationLayout.contentOffset.y let rawTitleOffset = -titleAreaHeight - presentationLayout.contentOffset.y
let titleOffset = max(-titleAreaHeight, rawTitleOffset) let titleOffset = max(-titleAreaHeight, rawTitleOffset)
let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: titleOffset), size: CGSize(width: size.width, height: 64.0))
transition.updateFrame(node: self.headerNode, frame: headerFrame)
let titleSize = self.contentTitleNode.measure(size) let titleSize = self.contentTitleNode.measure(size)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: titleOffset + 15.0), size: titleSize) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: 15.0), size: titleSize)
transition.updateFrame(node: self.contentTitleNode, frame: titleFrame) transition.updateFrame(node: self.contentTitleNode, frame: titleFrame)
let subtitleSize = self.contentSubtitleNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0, height: titleAreaHeight)) let subtitleSize = self.contentSubtitleNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0, height: titleAreaHeight))
let subtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: titleOffset + 40.0), size: subtitleSize) let subtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: 40.0), size: subtitleSize)
var originalSubtitleFrame = self.contentSubtitleNode.frame var originalSubtitleFrame = self.contentSubtitleNode.frame
originalSubtitleFrame.origin.x = subtitleFrame.origin.x originalSubtitleFrame.origin.x = subtitleFrame.origin.x
originalSubtitleFrame.size = subtitleFrame.size originalSubtitleFrame.size = subtitleFrame.size
@ -575,19 +601,19 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
transition.updateFrame(node: self.contentSubtitleNode, frame: subtitleFrame) transition.updateFrame(node: self.contentSubtitleNode, frame: subtitleFrame)
let titleButtonSize = CGSize(width: 44.0, height: 44.0) let titleButtonSize = CGSize(width: 44.0, height: 44.0)
let searchButtonFrame = CGRect(origin: CGPoint(x: 12.0, y: titleOffset + 12.0), size: titleButtonSize) let searchButtonFrame = CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: titleButtonSize)
transition.updateFrame(node: self.searchButtonNode, frame: searchButtonFrame) transition.updateFrame(node: self.searchButtonNode, frame: searchButtonFrame)
let shareButtonFrame = CGRect(origin: CGPoint(x: size.width - titleButtonSize.width - 12.0, y: titleOffset + 12.0), size: titleButtonSize) let shareButtonFrame = CGRect(origin: CGPoint(x: size.width - titleButtonSize.width - 12.0, y: 12.0), size: titleButtonSize)
transition.updateFrame(node: self.shareButtonNode, frame: shareButtonFrame) transition.updateFrame(node: self.shareButtonNode, frame: shareButtonFrame)
transition.updateFrame(node: self.shareContainerNode, frame: CGRect(origin: CGPoint(), size: titleButtonSize)) transition.updateFrame(node: self.shareContainerNode, frame: CGRect(origin: CGPoint(), size: titleButtonSize))
transition.updateFrame(node: self.shareReferenceNode, frame: CGRect(origin: CGPoint(), size: titleButtonSize)) transition.updateFrame(node: self.shareReferenceNode, frame: CGRect(origin: CGPoint(), size: titleButtonSize))
let segmentedSize = self.segmentedNode.updateLayout(.sizeToFit(maximumWidth: size.width - titleButtonSize.width * 2.0, minimumWidth: 160.0, height: 32.0), transition: transition) let segmentedSize = self.segmentedNode.updateLayout(.sizeToFit(maximumWidth: size.width - titleButtonSize.width * 2.0, minimumWidth: 160.0, height: 32.0), transition: transition)
transition.updateFrame(node: self.segmentedNode, frame: CGRect(origin: CGPoint(x: floor((size.width - segmentedSize.width) / 2.0), y: titleOffset + 18.0), size: segmentedSize)) transition.updateFrame(node: self.segmentedNode, frame: CGRect(origin: CGPoint(x: floor((size.width - segmentedSize.width) / 2.0), y: 18.0), size: segmentedSize))
let avatarButtonSize = CGSize(width: 36.0, height: 36.0) let avatarButtonSize = CGSize(width: 36.0, height: 36.0)
let avatarButtonFrame = CGRect(origin: CGPoint(x: size.width - avatarButtonSize.width - 20.0, y: titleOffset + 15.0), size: avatarButtonSize) let avatarButtonFrame = CGRect(origin: CGPoint(x: size.width - avatarButtonSize.width - 20.0, y: 15.0), size: avatarButtonSize)
transition.updateFrame(node: self.contentTitleAccountNode, frame: avatarButtonFrame) transition.updateFrame(node: self.contentTitleAccountNode, frame: avatarButtonFrame)
transition.updateFrame(node: self.contentSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: titleOffset + titleAreaHeight), size: CGSize(width: size.width, height: UIScreenPixel))) transition.updateFrame(node: self.contentSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: titleOffset + titleAreaHeight), size: CGSize(width: size.width, height: UIScreenPixel)))

View File

@ -63,6 +63,8 @@ final class ShareTopicGridItemNode: GridItemNode {
override init() { override init() {
self.iconView = ComponentView<Empty>() self.iconView = ComponentView<Empty>()
self.textNode = ImmediateTextNode() self.textNode = ImmediateTextNode()
self.textNode.maximumNumberOfLines = 2
self.textNode.textAlignment = .center
super.init() super.init()

View File

@ -165,7 +165,8 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
private var entries: [ShareTopicEntry] = [] private var entries: [ShareTopicEntry] = []
private var enqueuedTransitions: [(ShareGridTransaction, Bool)] = [] private var enqueuedTransitions: [(ShareGridTransaction, Bool)] = []
private let contentGridNode: GridNode let contentGridNode: GridNode
private let headerNode: ASDisplayNode
private let contentTitleNode: ASTextNode private let contentTitleNode: ASTextNode
private let contentSubtitleNode: ASTextNode private let contentSubtitleNode: ASTextNode
private let backNode: CancelButtonNode private let backNode: CancelButtonNode
@ -203,6 +204,7 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
} }
self.contentGridNode = GridNode() self.contentGridNode = GridNode()
self.headerNode = ASDisplayNode()
self.contentTitleNode = ASTextNode() self.contentTitleNode = ASTextNode()
self.contentTitleNode.attributedText = NSAttributedString(string: peer.compactDisplayTitle, font: Font.medium(20.0), textColor: self.theme.actionSheet.primaryTextColor) self.contentTitleNode.attributedText = NSAttributedString(string: peer.compactDisplayTitle, font: Font.medium(20.0), textColor: self.theme.actionSheet.primaryTextColor)
@ -219,10 +221,11 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
super.init() super.init()
self.addSubnode(self.contentGridNode) self.addSubnode(self.contentGridNode)
self.addSubnode(self.headerNode)
self.addSubnode(self.contentTitleNode) self.headerNode.addSubnode(self.contentTitleNode)
self.addSubnode(self.contentSubtitleNode) self.headerNode.addSubnode(self.contentSubtitleNode)
self.addSubnode(self.backNode) self.headerNode.addSubnode(self.backNode)
let previousItems = Atomic<[ShareTopicEntry]?>(value: []) let previousItems = Atomic<[ShareTopicEntry]?>(value: [])
self.disposable.set((items self.disposable.set((items
@ -304,7 +307,9 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
func deactivate() { func deactivate() {
} }
func animateIn(sourceFrame: CGRect) { func animateIn(sourceFrame: CGRect, scrollDelta: CGFloat) {
self.headerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: scrollDelta), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.backNode.alpha = 1.0 self.backNode.alpha = 1.0
self.backNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.backNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.backNode.layer.animatePosition(from: CGPoint(x: 20.0, y: 0.0), to: .zero, duration: 0.2, additive: true) self.backNode.layer.animatePosition(from: CGPoint(x: 20.0, y: 0.0), to: .zero, duration: 0.2, additive: true)
@ -319,6 +324,7 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
self.contentSubtitleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: .zero, duration: 0.2, additive: true) self.contentSubtitleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: .zero, duration: 0.2, additive: true)
self.contentSubtitleNode.layer.animateScale(from: 0.85, to: 1.0, duration: 0.2) self.contentSubtitleNode.layer.animateScale(from: 0.85, to: 1.0, duration: 0.2)
self.contentGridNode.layer.animatePosition(from: CGPoint(x: 0.0, y: scrollDelta), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.contentGridNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.contentGridNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.contentGridNode.forEachItemNode { itemNode in self.contentGridNode.forEachItemNode { itemNode in
@ -327,7 +333,9 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
} }
} }
func animateOut(targetFrame: CGRect, completion: @escaping () -> Void = {}) { func animateOut(targetFrame: CGRect, scrollDelta: CGFloat, completion: @escaping () -> Void = {}) {
self.headerNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: scrollDelta), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.backNode.alpha = 0.0 self.backNode.alpha = 0.0
self.backNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) self.backNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.backNode.layer.animatePosition(from: .zero, to: CGPoint(x: 20.0, y: 0.0), duration: 0.2, additive: true) self.backNode.layer.animatePosition(from: .zero, to: CGPoint(x: 20.0, y: 0.0), duration: 0.2, additive: true)
@ -342,13 +350,15 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
self.contentSubtitleNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 10.0), duration: 0.2, additive: true) self.contentSubtitleNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 10.0), duration: 0.2, additive: true)
self.contentSubtitleNode.layer.animateScale(from: 1.0, to: 0.85, duration: 0.2) self.contentSubtitleNode.layer.animateScale(from: 1.0, to: 0.85, duration: 0.2)
self.contentGridNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: scrollDelta), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.contentGridNode.alpha = 0.0 self.contentGridNode.alpha = 0.0
self.contentGridNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { _ in self.contentGridNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { _ in
completion() completion()
}) })
self.contentGridNode.forEachItemNode { itemNode in self.contentGridNode.forEachItemNode { itemNode in
itemNode.layer.animatePosition(from: itemNode.position, to: targetFrame.center, duration: 0.45, timingFunction: kCAMediaTimingFunctionSpring) itemNode.layer.animatePosition(from: itemNode.position, to: targetFrame.center, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
itemNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) itemNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
} }
} }
@ -412,19 +422,22 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
let rawTitleOffset = -titleAreaHeight - presentationLayout.contentOffset.y let rawTitleOffset = -titleAreaHeight - presentationLayout.contentOffset.y
let titleOffset = max(-titleAreaHeight, rawTitleOffset) let titleOffset = max(-titleAreaHeight, rawTitleOffset)
let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: titleOffset), size: CGSize(width: size.width, height: 64.0))
transition.updateFrame(node: self.headerNode, frame: headerFrame)
let titleSize = self.contentTitleNode.measure(size) let titleSize = self.contentTitleNode.measure(size)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: titleOffset + 15.0), size: titleSize) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: 15.0), size: titleSize)
transition.updateFrame(node: self.contentTitleNode, frame: titleFrame) transition.updateFrame(node: self.contentTitleNode, frame: titleFrame)
let subtitleSize = self.contentSubtitleNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0, height: titleAreaHeight)) let subtitleSize = self.contentSubtitleNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0, height: titleAreaHeight))
let subtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: titleOffset + 40.0), size: subtitleSize) let subtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: 40.0), size: subtitleSize)
var originalSubtitleFrame = self.contentSubtitleNode.frame var originalSubtitleFrame = self.contentSubtitleNode.frame
originalSubtitleFrame.origin.x = subtitleFrame.origin.x originalSubtitleFrame.origin.x = subtitleFrame.origin.x
originalSubtitleFrame.size = subtitleFrame.size originalSubtitleFrame.size = subtitleFrame.size
self.contentSubtitleNode.frame = originalSubtitleFrame self.contentSubtitleNode.frame = originalSubtitleFrame
transition.updateFrame(node: self.contentSubtitleNode, frame: subtitleFrame) transition.updateFrame(node: self.contentSubtitleNode, frame: subtitleFrame)
let backFrame = CGRect(origin: CGPoint(x: 30.0, y: titleOffset + 6.0), size: CGSize(width: 90.0, height: 56.0)) let backFrame = CGRect(origin: CGPoint(x: 30.0, y: 6.0), size: CGSize(width: 90.0, height: 56.0))
transition.updateFrame(node: self.backNode, frame: backFrame) transition.updateFrame(node: self.backNode, frame: backFrame)
self.contentOffsetUpdated?(presentationLayout.contentOffset.y, actualTransition) self.contentOffsetUpdated?(presentationLayout.contentOffset.y, actualTransition)