diff --git a/submodules/SparseItemGrid/Sources/SparseItemGrid.swift b/submodules/SparseItemGrid/Sources/SparseItemGrid.swift index d23b20ad8e..a436ba8c8b 100644 --- a/submodules/SparseItemGrid/Sources/SparseItemGrid.swift +++ b/submodules/SparseItemGrid/Sources/SparseItemGrid.swift @@ -35,7 +35,7 @@ public protocol SparseItemGridBinding: AnyObject { func createLayer() -> SparseItemGridLayer? func createView() -> SparseItemGridView? func createShimmerLayer() -> SparseItemGridShimmerLayer? - func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool) + func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: SparseItemGrid.Synchronous) func unbindLayer(layer: SparseItemGridLayer) func scrollerTextForTag(tag: Int32) -> String? func loadHole(anchor: SparseItemGrid.HoleAnchor, at location: SparseItemGrid.HoleLocation) -> Signal @@ -167,6 +167,12 @@ public final class SparseItemGrid: ASDisplayNode { } } + public enum Synchronous { + case semi + case full + case none + } + open class Item { open var id: AnyHashable { preconditionFailure() @@ -476,7 +482,7 @@ public final class SparseItemGrid: ASDisplayNode { self.view.addSubview(self.scrollView) } - func update(containerLayout: ContainerLayout, items: Items, restoreScrollPosition: (y: CGFloat, index: Int)?, synchronous: Bool) { + func update(containerLayout: ContainerLayout, items: Items, restoreScrollPosition: (y: CGFloat, index: Int)?, synchronous: SparseItemGrid.Synchronous) { if self.layout?.containerLayout != containerLayout || self.items !== items { self.layout = Layout(containerLayout: containerLayout, zoomLevel: self.zoomLevel) self.items = items @@ -497,7 +503,7 @@ public final class SparseItemGrid: ASDisplayNode { @objc func scrollViewDidScroll(_ scrollView: UIScrollView) { if !self.ignoreScrolling { - self.updateVisibleItems(resetScrolling: false, synchronous: true, restoreScrollPosition: nil) + self.updateVisibleItems(resetScrolling: false, synchronous: .full, restoreScrollPosition: nil) if let layout = self.layout, let _ = self.items { let offset = scrollView.contentOffset.y @@ -769,7 +775,7 @@ public final class SparseItemGrid: ASDisplayNode { self.decelerationAnimator?.isPaused = false } - private func updateVisibleItems(resetScrolling: Bool, synchronous: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) { + private func updateVisibleItems(resetScrolling: Bool, synchronous: SparseItemGrid.Synchronous, restoreScrollPosition: (y: CGFloat, index: Int)?) { guard let layout = self.layout, let items = self.items else { return } @@ -1270,7 +1276,7 @@ public final class SparseItemGrid: ASDisplayNode { }) nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size) - nextViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition, synchronous: false) + nextViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition, synchronous: .semi) self.currentViewportTransition?.removeFromSupernode() @@ -1320,7 +1326,7 @@ public final class SparseItemGrid: ASDisplayNode { }) nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size) - nextViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition, synchronous: false) + nextViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition, synchronous: .semi) let currentViewportTransition = ViewportTransition(interactiveState: interactiveState, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: nextViewport, coveringOffsetUpdated: { [weak self] transition in self?.transitionCoveringOffsetUpdated(transition: transition) @@ -1359,7 +1365,7 @@ public final class SparseItemGrid: ASDisplayNode { strongSelf.scrollingArea.frame = CGRect(origin: CGPoint(), size: containerLayout.size) currentViewport.setScrollingArea(scrollingArea: strongSelf.scrollingArea) currentViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size) - currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: nil, synchronous: false) + currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: nil, synchronous: .semi) } strongSelf.currentViewportTransition = nil @@ -1371,7 +1377,7 @@ public final class SparseItemGrid: ASDisplayNode { } } - public func update(size: CGSize, insets: UIEdgeInsets, scrollIndicatorInsets: UIEdgeInsets, lockScrollingAtTop: Bool, fixedItemHeight: CGFloat?, items: Items, theme: PresentationTheme, synchronous: Bool) { + public func update(size: CGSize, insets: UIEdgeInsets, scrollIndicatorInsets: UIEdgeInsets, lockScrollingAtTop: Bool, fixedItemHeight: CGFloat?, items: Items, theme: PresentationTheme, synchronous: SparseItemGrid.Synchronous) { self.theme = theme let containerLayout = ContainerLayout(size: size, insets: insets, scrollIndicatorInsets: scrollIndicatorInsets, lockScrollingAtTop: lockScrollingAtTop, fixedItemHeight: fixedItemHeight) self.containerLayout = containerLayout @@ -1483,7 +1489,7 @@ public final class SparseItemGrid: ASDisplayNode { self.scrollingArea.frame = CGRect(origin: CGPoint(), size: containerLayout.size) currentViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size) - currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition, synchronous: false) + currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition, synchronous: .semi) let currentViewportTransition = ViewportTransition(interactiveState: nil, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: currentViewport, coveringOffsetUpdated: { [weak self] transition in self?.transitionCoveringOffsetUpdated(transition: transition) @@ -1501,7 +1507,7 @@ public final class SparseItemGrid: ASDisplayNode { strongSelf.insertSubnode(currentViewport, belowSubnode: strongSelf.scrollingArea) strongSelf.scrollingArea.frame = CGRect(origin: CGPoint(), size: containerLayout.size) currentViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size) - currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: nil, synchronous: false) + currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: nil, synchronous: .semi) } strongSelf.currentViewport?.setScrollingArea(scrollingArea: strongSelf.scrollingArea) diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift index 3a8bcc7290..0a02e05d6b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift @@ -1119,7 +1119,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme return (list.map(\.0), list.map(\.1)) }() - func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool) { + func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: SparseItemGrid.Synchronous) { for i in 0 ..< items.count { guard let item = items[i] as? VisualMediaItem else { continue @@ -1170,22 +1170,37 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme } if let selectedMedia = selectedMedia { - if let result = directMediaImageCache.getImage(message: message, media: selectedMedia, width: imageWidthSpec, possibleWidths: SparseItemGridBindingImpl.widthSpecs.1, synchronous: synchronous) { + if let result = directMediaImageCache.getImage(message: message, media: selectedMedia, width: imageWidthSpec, possibleWidths: SparseItemGridBindingImpl.widthSpecs.1, synchronous: synchronous == .full) { if let image = result.image { layer.contents = image.cgImage layer.hasContents = true - if !synchronous { + switch synchronous { + case .none: layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + default: + break } } if let loadSignal = result.loadSignal { layer.disposable?.dispose() + let startTimestamp = CFAbsoluteTimeGetCurrent() layer.disposable = (loadSignal |> deliverOnMainQueue).start(next: { [weak self, weak layer, weak displayItem] image in guard let layer = layer else { return } - if layer.hasContents { + let deltaTime = CFAbsoluteTimeGetCurrent() - startTimestamp + let synchronousValue: Bool + switch synchronous { + case .none: + synchronousValue = false + case .semi: + synchronousValue = deltaTime < 0.1 + case .full: + synchronousValue = true + } + + if layer.hasContents && !synchronousValue { let copyLayer = ItemLayer() copyLayer.contents = layer.contents copyLayer.contentsRect = layer.contentsRect @@ -1201,7 +1216,9 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme layer.contents = image?.cgImage layer.hasContents = true - layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + if !synchronousValue { + layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } if let displayItem = displayItem { self?.updateShimmerLayersImpl?(displayItem) @@ -2145,7 +2162,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro fixedItemHeight = nil } - self.itemGrid.update(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), scrollIndicatorInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: bottomInset, right: sideInset), lockScrollingAtTop: isScrollingLockedAtTop, fixedItemHeight: fixedItemHeight, items: items, theme: self.itemGridBinding.chatPresentationData.theme.theme, synchronous: wasFirstTime) + self.itemGrid.update(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), scrollIndicatorInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: bottomInset, right: sideInset), lockScrollingAtTop: isScrollingLockedAtTop, fixedItemHeight: fixedItemHeight, items: items, theme: self.itemGridBinding.chatPresentationData.theme.theme, synchronous: wasFirstTime ? .full : .none) } }