Completely replace all items when jumping to present
This commit is contained in:
parent
0485400c1f
commit
80f9800fd6
|
@ -343,21 +343,19 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
}
|
||||
|
||||
private func insertPresentItemsIfNecessary(_ presentItems: [String]) {
|
||||
var snapshot = dataSource.snapshot()
|
||||
let snapshot = dataSource.snapshot()
|
||||
guard snapshot.indexOfSection(.statuses) != nil else {
|
||||
return
|
||||
}
|
||||
let currentItems = snapshot.itemIdentifiers(inSection: .statuses)
|
||||
if case .status(id: let firstID, state: _) = currentItems.first,
|
||||
// if there's no overlap between presentItems and the existing items in the data source, insert the present items and prompt the user
|
||||
// if there's no overlap between presentItems and the existing items in the data source, prompt the user
|
||||
!presentItems.contains(firstID) {
|
||||
|
||||
// remove any existing gap, if there is one
|
||||
if let index = currentItems.lastIndex(of: .gap) {
|
||||
snapshot.deleteItems(Array(currentItems[index...]))
|
||||
}
|
||||
snapshot.insertItems([.gap], beforeItem: currentItems.first!)
|
||||
snapshot.insertItems(presentItems.map { .status(id: $0, state: .unknown) }, beforeItem: .gap)
|
||||
// create a new snapshot to reset the timeline to the "present" state
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
snapshot.appendSections([.statuses])
|
||||
snapshot.appendItems(presentItems.map { .status(id: $0, state: .unknown) }, toSection: .statuses)
|
||||
|
||||
var config = ToastConfiguration(title: "Jump to present")
|
||||
config.edge = .top
|
||||
|
@ -366,12 +364,34 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
config.action = { [unowned self] toast in
|
||||
toast.dismissToast(animated: true)
|
||||
|
||||
let origSnapshot = self.dataSource.snapshot()
|
||||
let origItemAtTop: (Item, CGFloat)?
|
||||
if let statusesSection = origSnapshot.indexOfSection(.statuses),
|
||||
let indexPath = self.collectionView.indexPathsForVisibleItems.sorted().first(where: { $0.section == statusesSection }),
|
||||
let cell = self.collectionView.cellForItem(at: indexPath),
|
||||
let item = self.dataSource.itemIdentifier(for: indexPath) {
|
||||
origItemAtTop = (item, cell.convert(.zero, to: self.view).y - self.view.safeAreaInsets.top)
|
||||
} else {
|
||||
origItemAtTop = nil
|
||||
}
|
||||
|
||||
self.dataSource.apply(snapshot, animatingDifferences: true) {
|
||||
// TODO: we can't set prevScrollOffsetBeforeScrollToTop here to allow undoing the scroll-to-top
|
||||
// because that would involve scrolling through unmeasured-cell which fucks up the content offset values.
|
||||
// we probably need a data-source aware implementation of scrollToTop which uses item & offset w/in item
|
||||
// to track the restore position
|
||||
self.collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
|
||||
|
||||
var config = ToastConfiguration(title: "Go back")
|
||||
config.edge = .top
|
||||
config.systemImageName = "arrow.down"
|
||||
config.dismissAutomaticallyAfter = 4
|
||||
config.action = { [unowned self] toast in
|
||||
toast.dismissToast(animated: true)
|
||||
// todo: it would be nice if we could animate this, but that doesn't work with the screen-position-maintaining stuff
|
||||
if let (item, offset) = origItemAtTop {
|
||||
self.applySnapshot(snapshot, maintainingScreenPosition: offset, ofItem: item)
|
||||
} else {
|
||||
self.dataSource.apply(origSnapshot, animatingDifferences: false)
|
||||
}
|
||||
}
|
||||
self.showToast(configuration: config, animated: true)
|
||||
}
|
||||
}
|
||||
self.showToast(configuration: config, animated: true)
|
||||
|
@ -380,32 +400,35 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
|
||||
// NOTE: this only works when items are being inserted ABOVE the item to maintain
|
||||
private func applySnapshot(_ snapshot: NSDiffableDataSourceSnapshot<Section, Item>, maintainingBottomRelativeScrollPositionOf itemToMaintain: Item) {
|
||||
// use a snapshot of the collection view to hide the flicker as the content offset changes and then changes back
|
||||
let snapshotView = collectionView.snapshotView(afterScreenUpdates: false)!
|
||||
snapshotView.layer.zPosition = 1000
|
||||
snapshotView.frame = view.bounds
|
||||
view.addSubview(snapshotView)
|
||||
|
||||
var firstItemAfterOriginalGapOffsetFromTop: CGFloat = 0
|
||||
if let indexPath = dataSource.indexPath(for: itemToMaintain),
|
||||
let cell = collectionView.cellForItem(at: indexPath) {
|
||||
// subtract top safe area inset b/c scrollToItem at .top aligns the top of the cell to the top of the safe area
|
||||
firstItemAfterOriginalGapOffsetFromTop = cell.convert(.zero, to: view).y - view.safeAreaInsets.top
|
||||
}
|
||||
applySnapshot(snapshot, maintainingScreenPosition: firstItemAfterOriginalGapOffsetFromTop, ofItem: itemToMaintain)
|
||||
}
|
||||
|
||||
private func applySnapshot(_ snapshot: NSDiffableDataSourceSnapshot<Section, Item>, maintainingScreenPosition offsetFromTop: CGFloat, ofItem itemToMaintain: Item) {
|
||||
// use a snapshot of the collection view to hide the flicker as the content offset changes and then changes back
|
||||
let snapshotView = collectionView.snapshotView(afterScreenUpdates: false)!
|
||||
snapshotView.layer.zPosition = 1000
|
||||
snapshotView.frame = view.bounds
|
||||
view.addSubview(snapshotView)
|
||||
|
||||
dataSource.apply(snapshot, animatingDifferences: false) {
|
||||
if let indexPathOfItemAfterOriginalGap = self.dataSource.indexPath(for: itemToMaintain) {
|
||||
if let indexPathOfItemToMaintain = self.dataSource.indexPath(for: itemToMaintain) {
|
||||
// scroll up until we've accumulated enough MEASURED height that we can put the
|
||||
// firstItemAfterOriginalGapCell at the top of the screen and then scroll down by
|
||||
// firstItemAfterOriginalGapOffsetFromTop without intruding into unmeasured area
|
||||
var cur = indexPathOfItemAfterOriginalGap
|
||||
var cur = indexPathOfItemToMaintain
|
||||
var amountScrolledUp: CGFloat = 0
|
||||
while true {
|
||||
if cur.row <= 0 {
|
||||
break
|
||||
}
|
||||
if let cell = self.collectionView.cellForItem(at: indexPathOfItemAfterOriginalGap),
|
||||
cell.convert(.zero, to: self.view).y - self.view.safeAreaInsets.top > firstItemAfterOriginalGapOffsetFromTop {
|
||||
if let cell = self.collectionView.cellForItem(at: indexPathOfItemToMaintain),
|
||||
cell.convert(.zero, to: self.view).y - self.view.safeAreaInsets.top > offsetFromTop {
|
||||
break
|
||||
}
|
||||
cur = IndexPath(row: cur.row - 1, section: cur.section)
|
||||
|
@ -415,7 +438,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
amountScrolledUp += attrs.size.height
|
||||
}
|
||||
self.collectionView.contentOffset.y += amountScrolledUp
|
||||
self.collectionView.contentOffset.y -= firstItemAfterOriginalGapOffsetFromTop
|
||||
self.collectionView.contentOffset.y -= offsetFromTop
|
||||
}
|
||||
|
||||
snapshotView.removeFromSuperview()
|
||||
|
|
Loading…
Reference in New Issue