diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index 947d3cb4c8..435df4d5bc 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -73,7 +73,7 @@ public final class SelectablePeerNode: ASDisplayNode { private let avatarNode: AvatarNode private let onlineNode: PeerOnlineMarkerNode private var checkNode: CheckNode? - private let textNode: ASTextNode + private let textNode: ImmediateTextNode private let iconView: ComponentView @@ -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.isLayerBacked = !smartInvertColorsEnabled() - self.textNode = ASTextNode() + self.textNode = ImmediateTextNode() self.textNode.isUserInteractionEnabled = 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)) 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 leftOrigin = floorToScreenPixels((bounds.width - textSize.width) / 2.0) if let iconView = self.iconView.view, iconView.superview != nil { totalWidth += iconView.frame.width + 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 } - 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 avatarContainerFrame = self.avatarNodeContainer.frame diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index 86ce339e8c..a968a5e7aa 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -392,39 +392,53 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } strongSelf.topicsContentNode = topicsContentNode strongSelf.contentNode?.supernode?.addSubnode(topicsContentNode) - topicsContentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in - self?.contentNodeOffsetUpdated(contentOffset, transition: transition) - }) if let (layout, navigationBarHeight, _) = strongSelf.containerLayout { strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } - if let sourceFrame = strongSelf.peersContentNode?.animateOut(peerId: peer.peerId) { - topicsContentNode.animateIn(sourceFrame: sourceFrame) + if let peersContentNode = strongSelf.peersContentNode { + 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) { if let topicsContentNode = self.topicsContentNode, let peersContentNode = self.peersContentNode { + topicsContentNode.setContentOffsetUpdated(nil) 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 { self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) } - - if let targetFrame = self.peersContentNode?.animateIn(peerId: peerId), let topicsContentNode = self.topicsContentNode { - topicsContentNode.animateOut(targetFrame: targetFrame, completion: { [weak self] in - if let topicsContentNode = self?.topicsContentNode { - topicsContentNode.removeFromSupernode() - self?.topicsContentNode = nil - } + + if let peersContentNode = self.peersContentNode { + 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 { + topicsContentNode.removeFromSupernode() + self?.topicsContentNode = nil + } + }) + } } } diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift index 654b4daad9..b51b6d8a5d 100644 --- a/submodules/ShareController/Sources/SharePeersContainerNode.swift +++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift @@ -115,7 +115,8 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { private var entries: [SharePeerEntry] = [] private var enqueuedTransitions: [(ShareGridTransaction, Bool)] = [] - private let contentGridNode: GridNode + let contentGridNode: GridNode + private let headerNode: ASDisplayNode private let contentTitleNode: ASTextNode private let contentSubtitleNode: ASTextNode private let contentTitleAccountNode: AvatarNode @@ -193,6 +194,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { } self.contentGridNode = GridNode() + self.headerNode = ASDisplayNode() self.contentTitleNode = ASTextNode() 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() self.addSubnode(self.contentGridNode) + self.addSubnode(self.headerNode) - self.addSubnode(self.contentTitleNode) - self.addSubnode(self.contentSubtitleNode) - self.addSubnode(self.contentTitleAccountNode) - self.addSubnode(self.segmentedNode) - self.addSubnode(self.searchButtonNode) + self.headerNode.addSubnode(self.contentTitleNode) + self.headerNode.addSubnode(self.contentSubtitleNode) + self.headerNode.addSubnode(self.contentTitleAccountNode) + self.headerNode.addSubnode(self.segmentedNode) + self.headerNode.addSubnode(self.searchButtonNode) self.shareContainerNode.addSubnode(self.shareReferenceNode) self.shareButtonNode.addSubnode(self.shareContainerNode) - self.addSubnode(self.shareButtonNode) + self.headerNode.addSubnode(self.shareButtonNode) + self.addSubnode(self.contentSeparatorNode) self.shareContainerNode.activated = { [weak self] gesture, _ in @@ -367,7 +371,11 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { 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? { @@ -385,10 +393,16 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { })?.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.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.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.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) @@ -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.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 { - let sourceCenter = targetFrame.center let clippedNode = ASDisplayNode() clippedNode.clipsToBounds = true 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)) 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() maskView.frame = clippedNode.bounds @@ -415,33 +432,31 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { maskImageView.image = generateMaskImage() maskImageView.frame = maskView.bounds.offsetBy(dx: 0.0, dy: 36.0) maskView.addSubview(maskImageView) - clippedNode.view.mask = maskView + self.contentGridNode.alpha = 1.0 self.contentGridNode.forEachItemNode { itemNode in - if let snapshotView = itemNode.view.snapshotView(afterScreenUpdates: false) { + 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.animateScale(from: 1.35, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak clippedNode] _ in + clippedNode?.view.removeFromSuperview() + }) + } else 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 { - 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 - clippedNode?.view.removeFromSuperview() - }) - } else { - clippedNode.view.addSubview(snapshotView) - - itemNode.alpha = 0.0 - let angle = sourceCenter.angle(to: itemNode.position) - let distance = sourceCenter.distance(to: itemNode.position) - let newDistance = distance * 2.8 - let newPosition = snapshotView.center.offsetBy(distance: newDistance, inDirection: angle) - snapshotView.layer.animatePosition(from: newPosition, to: snapshotView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - snapshotView.layer.animateScale(from: 1.35, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak itemNode] _ in - itemNode?.alpha = 1.0 - }) - snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false) - } + clippedNode.view.addSubview(snapshotView) + + itemNode.alpha = 0.0 + let angle = targetFrame.center.angle(to: itemNode.position) + let distance = targetFrame.center.distance(to: itemNode.position) + let newDistance = distance * 2.8 + let newPosition = snapshotView.center.offsetBy(distance: newDistance, inDirection: angle) + snapshotView.layer.animatePosition(from: newPosition, to: snapshotView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + snapshotView.layer.animateScale(from: 1.35, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak itemNode] _ in + itemNode?.alpha = 1.0 + }) + snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false) } } @@ -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.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.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.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) @@ -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.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 { - let sourceCenter = sourceFrame.center let clippedNode = ASDisplayNode() clippedNode.clipsToBounds = true 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)) 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() maskView.frame = clippedNode.bounds @@ -481,7 +505,6 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { maskImageView.image = generateMaskImage() maskImageView.frame = maskView.bounds.offsetBy(dx: 0.0, dy: 36.0) maskView.addSubview(maskImageView) - clippedNode.view.mask = maskView self.contentGridNode.forEachItemNode { itemNode in @@ -492,8 +515,8 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { if let itemNode = itemNode as? ShareControllerPeerGridItemNode, itemNode.peerId == peerId { } else { - let angle = sourceCenter.angle(to: itemNode.position) - let distance = sourceCenter.distance(to: itemNode.position) + let angle = sourceFrame.center.angle(to: itemNode.position) + let distance = sourceFrame.center.distance(to: itemNode.position) let newDistance = distance * 2.8 let newPosition = snapshotView.center.offsetBy(distance: newDistance, inDirection: angle) 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 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 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) 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 originalSubtitleFrame.origin.x = subtitleFrame.origin.x originalSubtitleFrame.size = subtitleFrame.size @@ -575,19 +601,19 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { transition.updateFrame(node: self.contentSubtitleNode, frame: subtitleFrame) 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) - 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.shareContainerNode, 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) - 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 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.contentSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: titleOffset + titleAreaHeight), size: CGSize(width: size.width, height: UIScreenPixel))) diff --git a/submodules/ShareController/Sources/ShareTopicGridItem.swift b/submodules/ShareController/Sources/ShareTopicGridItem.swift index 20047d4b1c..d795e3b446 100644 --- a/submodules/ShareController/Sources/ShareTopicGridItem.swift +++ b/submodules/ShareController/Sources/ShareTopicGridItem.swift @@ -63,6 +63,8 @@ final class ShareTopicGridItemNode: GridItemNode { override init() { self.iconView = ComponentView() self.textNode = ImmediateTextNode() + self.textNode.maximumNumberOfLines = 2 + self.textNode.textAlignment = .center super.init() diff --git a/submodules/ShareController/Sources/ShareTopicsContainerNode.swift b/submodules/ShareController/Sources/ShareTopicsContainerNode.swift index 9e5c77cb85..1ab6f849cf 100644 --- a/submodules/ShareController/Sources/ShareTopicsContainerNode.swift +++ b/submodules/ShareController/Sources/ShareTopicsContainerNode.swift @@ -165,7 +165,8 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { private var entries: [ShareTopicEntry] = [] private var enqueuedTransitions: [(ShareGridTransaction, Bool)] = [] - private let contentGridNode: GridNode + let contentGridNode: GridNode + private let headerNode: ASDisplayNode private let contentTitleNode: ASTextNode private let contentSubtitleNode: ASTextNode private let backNode: CancelButtonNode @@ -203,6 +204,7 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { } self.contentGridNode = GridNode() + self.headerNode = ASDisplayNode() self.contentTitleNode = ASTextNode() 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() self.addSubnode(self.contentGridNode) + self.addSubnode(self.headerNode) - self.addSubnode(self.contentTitleNode) - self.addSubnode(self.contentSubtitleNode) - self.addSubnode(self.backNode) + self.headerNode.addSubnode(self.contentTitleNode) + self.headerNode.addSubnode(self.contentSubtitleNode) + self.headerNode.addSubnode(self.backNode) let previousItems = Atomic<[ShareTopicEntry]?>(value: []) self.disposable.set((items @@ -304,7 +307,9 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { 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.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) @@ -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.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.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.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) @@ -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.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.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { _ in completion() }) 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) } } @@ -412,19 +422,22 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { let rawTitleOffset = -titleAreaHeight - presentationLayout.contentOffset.y 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 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) 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 originalSubtitleFrame.origin.x = subtitleFrame.origin.x originalSubtitleFrame.size = subtitleFrame.size self.contentSubtitleNode.frame = originalSubtitleFrame 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) self.contentOffsetUpdated?(presentationLayout.contentOffset.y, actualTransition)