Shared media improvements

This commit is contained in:
Ali
2021-10-25 17:00:32 +04:00
parent 66338027e7
commit 80fe21681e
6 changed files with 1637 additions and 58 deletions

View File

@@ -26,9 +26,14 @@ public protocol SparseItemGridDisplayItem: AnyObject {
var view: SparseItemGridView? { get }
}
public protocol SparseItemGridShimmerLayer: CALayer {
func update(size: CGSize)
}
public protocol SparseItemGridBinding: AnyObject {
func createLayer() -> SparseItemGridLayer?
func createView() -> SparseItemGridView?
func createShimmerLayer() -> SparseItemGridShimmerLayer?
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool)
func unbindLayer(layer: SparseItemGridLayer)
func scrollerTextForTag(tag: Int32) -> String?
@@ -141,10 +146,13 @@ private final class Shimmer {
}
}
final class Layer: CALayer {
final class Layer: CALayer, SparseItemGridShimmerLayer {
override func action(forKey event: String) -> CAAction? {
return nullAction
}
func update(size: CGSize) {
}
}
}
@@ -423,7 +431,7 @@ public final class SparseItemGrid: ASDisplayNode {
var layout: Layout?
var items: Items?
var visibleItems: [AnyHashable: VisibleItem] = [:]
var visiblePlaceholders: [Shimmer.Layer] = []
var visiblePlaceholders: [SparseItemGridShimmerLayer] = []
private var scrollingArea: SparseItemGridScrollingArea?
private var currentScrollingTag: Int32?
@@ -435,9 +443,12 @@ public final class SparseItemGrid: ASDisplayNode {
private var previousScrollOffset: CGFloat = 0.0
var coveringInsetOffset: CGFloat = 0.0
init(zoomLevel: ZoomLevel, maybeLoadHoleAnchor: @escaping (HoleAnchor, HoleLocation) -> Void) {
let coveringOffsetUpdated: (Viewport, ContainedViewLayoutTransition) -> Void
init(zoomLevel: ZoomLevel, maybeLoadHoleAnchor: @escaping (HoleAnchor, HoleLocation) -> Void, coveringOffsetUpdated: @escaping (Viewport, ContainedViewLayoutTransition) -> Void) {
self.zoomLevel = zoomLevel
self.maybeLoadHoleAnchor = maybeLoadHoleAnchor
self.coveringOffsetUpdated = coveringOffsetUpdated
self.scrollView = UIScrollView()
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
@@ -476,7 +487,7 @@ public final class SparseItemGrid: ASDisplayNode {
if !self.ignoreScrolling {
self.updateVisibleItems(resetScrolling: false, synchronous: true, restoreScrollPosition: nil)
if let layout = self.layout, let items = self.items {
if let layout = self.layout, let _ = self.items {
let offset = scrollView.contentOffset.y
let delta = offset - self.previousScrollOffset
self.previousScrollOffset = offset
@@ -495,7 +506,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
if coveringInsetOffset < self.coveringInsetOffset {
self.coveringInsetOffset = coveringInsetOffset
items.itemBinding.coveringInsetOffsetUpdated(transition: .immediate)
self.coveringOffsetUpdated(self, .immediate)
}
}
} else {
@@ -511,7 +522,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
if coveringInsetOffset != self.coveringInsetOffset {
self.coveringInsetOffset = coveringInsetOffset
items.itemBinding.coveringInsetOffsetUpdated(transition: .immediate)
self.coveringOffsetUpdated(self, .immediate)
}
}
}
@@ -539,7 +550,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
private func snapCoveringInsetOffset() {
if let layout = self.layout, let items = self.items {
if let layout = self.layout, let _ = self.items {
let offset = self.scrollView.contentOffset.y
if offset < layout.containerLayout.insets.top {
if offset <= layout.containerLayout.insets.top / 2.0 {
@@ -560,7 +571,7 @@ public final class SparseItemGrid: ASDisplayNode {
if coveringInsetOffset != self.coveringInsetOffset {
self.coveringInsetOffset = coveringInsetOffset
items.itemBinding.coveringInsetOffsetUpdated(transition: .animated(duration: 0.2, curve: .easeInOut))
self.coveringOffsetUpdated(self, .animated(duration: 0.2, curve: .easeInOut))
}
}
}
@@ -680,6 +691,10 @@ public final class SparseItemGrid: ASDisplayNode {
self.scrollView.setContentOffset(self.scrollView.contentOffset, animated: false)
}
func updateShimmerLayers() {
self.updateVisibleItems(resetScrolling: false, synchronous: false, restoreScrollPosition: nil)
}
private func updateVisibleItems(resetScrolling: Bool, synchronous: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) {
guard let layout = self.layout, let items = self.items else {
return
@@ -753,21 +768,39 @@ public final class SparseItemGrid: ASDisplayNode {
}
}
if itemLayer.needsShimmer {
let placeholderLayer: SparseItemGridShimmerLayer
if self.visiblePlaceholders.count > usedPlaceholderCount {
placeholderLayer = self.visiblePlaceholders[usedPlaceholderCount]
} else {
placeholderLayer = items.itemBinding.createShimmerLayer() ?? Shimmer.Layer()
self.scrollView.layer.insertSublayer(placeholderLayer, at: 0)
self.visiblePlaceholders.append(placeholderLayer)
}
let itemFrame = layout.frame(at: index)
placeholderLayer.frame = itemFrame
self.shimmer.update(colors: shimmerColors, layer: placeholderLayer, containerSize: layout.containerLayout.size, frame: itemFrame.offsetBy(dx: 0.0, dy: -visibleBounds.minY))
placeholderLayer.update(size: itemFrame.size)
usedPlaceholderCount += 1
}
validIds.insert(item.id)
itemLayer.frame = layout.frame(at: index)
} else if layout.containerLayout.fixedItemHeight == nil {
let placeholderLayer: Shimmer.Layer
} else {
let placeholderLayer: SparseItemGridShimmerLayer
if self.visiblePlaceholders.count > usedPlaceholderCount {
placeholderLayer = self.visiblePlaceholders[usedPlaceholderCount]
} else {
placeholderLayer = Shimmer.Layer()
placeholderLayer = items.itemBinding.createShimmerLayer() ?? Shimmer.Layer()
self.scrollView.layer.addSublayer(placeholderLayer)
self.visiblePlaceholders.append(placeholderLayer)
}
let itemFrame = layout.frame(at: index)
placeholderLayer.frame = itemFrame
self.shimmer.update(colors: shimmerColors, layer: placeholderLayer, containerSize: layout.containerLayout.size, frame: itemFrame.offsetBy(dx: 0.0, dy: -visibleBounds.minY))
placeholderLayer.update(size: itemFrame.size)
usedPlaceholderCount += 1
}
}
@@ -783,11 +816,6 @@ public final class SparseItemGrid: ASDisplayNode {
} else if let view = item.view {
view.update(size: layer.frame.size)
}
if item.needsShimmer {
let itemFrame = layer.frame
self.shimmer.update(colors: shimmerColors, layer: item.displayLayer, containerSize: layout.containerLayout.size, frame: itemFrame.offsetBy(dx: 0.0, dy: -visibleBounds.minY))
}
}
}
@@ -923,12 +951,19 @@ public final class SparseItemGrid: ASDisplayNode {
var currentProgress: CGFloat = 0.0
init(interactiveState: InteractiveState?, layout: ContainerLayout, anchorItemIndex: Int, from fromViewport: Viewport, to toViewport: Viewport) {
var coveringInsetOffset: CGFloat {
return self.fromViewport.coveringInsetOffset * (1.0 - self.currentProgress) + self.toViewport.coveringInsetOffset * self.currentProgress
}
let coveringOffsetUpdated: (ContainedViewLayoutTransition) -> Void
init(interactiveState: InteractiveState?, layout: ContainerLayout, anchorItemIndex: Int, from fromViewport: Viewport, to toViewport: Viewport, coveringOffsetUpdated: @escaping (ContainedViewLayoutTransition) -> Void) {
self.interactiveState = interactiveState
self.layout = layout
self.anchorItemIndex = anchorItemIndex
self.fromViewport = fromViewport
self.toViewport = toViewport
self.coveringOffsetUpdated = coveringOffsetUpdated
super.init()
@@ -1006,6 +1041,8 @@ public final class SparseItemGrid: ASDisplayNode {
} else {
transition.updateAlpha(node: self.fromViewport, alpha: 1.0 - fromAlphaProgress)
}
self.coveringOffsetUpdated(transition)
}
}
@@ -1033,12 +1070,17 @@ public final class SparseItemGrid: ASDisplayNode {
private let loadingHoleDisposable = MetaDisposable()
public var coveringInsetOffset: CGFloat {
if let currentViewport = self.currentViewport {
if let currentViewportTransition = self.currentViewportTransition {
return currentViewportTransition.coveringInsetOffset
} else if let currentViewport = self.currentViewport {
return currentViewport.coveringInsetOffset
} else {
return 0.0
}
return 0.0
}
public var cancelExternalContentGestures: (() -> Void)?
override public init() {
self.scrollingArea = SparseItemGridScrollingArea()
@@ -1085,7 +1127,7 @@ public final class SparseItemGrid: ASDisplayNode {
switch recognizer.state {
case .began:
break
self.cancelExternalContentGestures?()
case .changed:
let scale = recognizer.scale
if let currentViewportTransition = self.currentViewportTransition, let interactiveState = currentViewportTransition.interactiveState {
@@ -1138,6 +1180,8 @@ public final class SparseItemGrid: ASDisplayNode {
return
}
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
})
nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
@@ -1146,7 +1190,9 @@ public final class SparseItemGrid: ASDisplayNode {
self.currentViewportTransition?.removeFromSupernode()
let nextInteractiveState = ViewportTransition.InteractiveState(anchorLocation: anchorLocation, initialScale: startScale, targetScale: nextScale)
let currentViewportTransition = ViewportTransition(interactiveState: nextInteractiveState, layout: containerLayout, anchorItemIndex: currentViewportTransition.anchorItemIndex, from: boundaryViewport, to: nextViewport)
let currentViewportTransition = ViewportTransition(interactiveState: nextInteractiveState, layout: containerLayout, anchorItemIndex: currentViewportTransition.anchorItemIndex, from: boundaryViewport, to: nextViewport, coveringOffsetUpdated: { [weak self] transition in
self?.transitionCoveringOffsetUpdated(transition: transition)
})
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
self.currentViewportTransition = currentViewportTransition
@@ -1184,12 +1230,16 @@ public final class SparseItemGrid: ASDisplayNode {
return
}
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
})
nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
nextViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition)
let currentViewportTransition = ViewportTransition(interactiveState: interactiveState, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: nextViewport)
let currentViewportTransition = ViewportTransition(interactiveState: interactiveState, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: nextViewport, coveringOffsetUpdated: { [weak self] transition in
self?.transitionCoveringOffsetUpdated(transition: transition)
})
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
self.currentViewportTransition = currentViewportTransition
@@ -1249,6 +1299,8 @@ public final class SparseItemGrid: ASDisplayNode {
return
}
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
})
self.currentViewport = currentViewport
self.insertSubnode(currentViewport, belowSubnode: self.scrollingArea)
@@ -1272,6 +1324,12 @@ public final class SparseItemGrid: ASDisplayNode {
return
}
/*#if DEBUG
if "".isEmpty {
return
}
#endif*/
self.isLoadingHole = true
self.loadingHoleDisposable.set((items.itemBinding.loadHole(anchor: holeAnchor, at: location)
|> deliverOnMainQueue).start(completed: { [weak self] in
@@ -1323,6 +1381,8 @@ public final class SparseItemGrid: ASDisplayNode {
return
}
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
})
self.currentViewport = currentViewport
self.insertSubnode(currentViewport, belowSubnode: self.scrollingArea)
@@ -1337,7 +1397,9 @@ public final class SparseItemGrid: ASDisplayNode {
currentViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition)
let currentViewportTransition = ViewportTransition(interactiveState: nil, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: currentViewport)
let currentViewportTransition = ViewportTransition(interactiveState: nil, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: currentViewport, coveringOffsetUpdated: { [weak self] transition in
self?.transitionCoveringOffsetUpdated(transition: transition)
})
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
self.currentViewportTransition = currentViewportTransition
@@ -1365,6 +1427,23 @@ public final class SparseItemGrid: ASDisplayNode {
}
}
private func coveringOffsetUpdated(viewport: Viewport, transition: ContainedViewLayoutTransition) {
guard let items = self.items else {
return
}
if self.currentViewportTransition != nil {
return
}
items.itemBinding.coveringInsetOffsetUpdated(transition: transition)
}
private func transitionCoveringOffsetUpdated(transition: ContainedViewLayoutTransition) {
guard let items = self.items else {
return
}
items.itemBinding.coveringInsetOffsetUpdated(transition: transition)
}
public func forEachVisibleItem(_ f: (SparseItemGridDisplayItem) -> Void) {
guard let currentViewport = self.currentViewport else {
return
@@ -1420,4 +1499,12 @@ public final class SparseItemGrid: ASDisplayNode {
self.scrollingArea.hideScroller()
}
public func updateShimmerLayers() {
self.currentViewport?.updateShimmerLayers()
if let currentViewportTransition = self.currentViewportTransition {
currentViewportTransition.fromViewport.updateShimmerLayers()
currentViewportTransition.toViewport.updateShimmerLayers()
}
}
}