Compare commits
No commits in common. "47b838a3867325344779b7a47f2bb4214c50584a" and "0a8d50cc27dc91c321f7962d7ab3a19babe607df" have entirely different histories.
47b838a386
...
0a8d50cc27
|
@ -17,16 +17,17 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
|
|
||||||
@IBOutlet weak var scrollView: UIScrollView!
|
@IBOutlet weak var scrollView: UIScrollView!
|
||||||
@IBOutlet weak var topControlsView: UIView!
|
@IBOutlet weak var topControlsView: UIView!
|
||||||
|
@IBOutlet weak var topControlsHeightConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var shareButton: UIButton!
|
||||||
|
@IBOutlet weak var shareButtonTopConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var shareButtonLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var closeButton: UIButton!
|
||||||
|
@IBOutlet weak var closeButtonTopConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var closeButtonTrailingConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
@IBOutlet weak var bottomControlsView: UIView!
|
@IBOutlet weak var bottomControlsView: UIView!
|
||||||
@IBOutlet weak var descriptionLabel: UILabel!
|
@IBOutlet weak var descriptionLabel: UILabel!
|
||||||
|
|
||||||
private var shareContainer: UIView!
|
|
||||||
private var shareImage: UIImageView!
|
|
||||||
private var shareButtonTopConstraint: NSLayoutConstraint!
|
|
||||||
private var shareButtonLeadingConstraint: NSLayoutConstraint!
|
|
||||||
private var closeButtonTopConstraint: NSLayoutConstraint!
|
|
||||||
private var closeButtonTrailingConstraint: NSLayoutConstraint!
|
|
||||||
|
|
||||||
var contentView: LargeImageContentView {
|
var contentView: LargeImageContentView {
|
||||||
didSet {
|
didSet {
|
||||||
oldValue.removeFromSuperview()
|
oldValue.removeFromSuperview()
|
||||||
|
@ -85,13 +86,9 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
setupContentView()
|
setupContentView()
|
||||||
setupControls()
|
|
||||||
|
|
||||||
setControlsVisible(initialControlsVisible, animated: false)
|
setControlsVisible(initialControlsVisible, animated: false)
|
||||||
if contentView.activityItemsForSharing.isEmpty {
|
shareButton.isEnabled = !contentView.activityItemsForSharing.isEmpty
|
||||||
shareContainer.isUserInteractionEnabled = false
|
|
||||||
shareImage.tintColor = .systemGray
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollView.delegate = self
|
scrollView.delegate = self
|
||||||
|
|
||||||
|
@ -129,62 +126,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupControls() {
|
|
||||||
shareContainer = UIView()
|
|
||||||
shareContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(sharePressed)))
|
|
||||||
shareContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
topControlsView.addSubview(shareContainer)
|
|
||||||
shareImage = UIImageView(image: UIImage(systemName: "square.and.arrow.up"))
|
|
||||||
shareImage.tintColor = .white
|
|
||||||
shareImage.contentMode = .scaleAspectFit
|
|
||||||
shareImage.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
shareContainer.addSubview(shareImage)
|
|
||||||
shareButtonTopConstraint = shareImage.topAnchor.constraint(greaterThanOrEqualTo: shareContainer.topAnchor)
|
|
||||||
shareButtonLeadingConstraint = shareImage.leadingAnchor.constraint(greaterThanOrEqualTo: shareContainer.leadingAnchor)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
shareContainer.topAnchor.constraint(equalTo: topControlsView.topAnchor),
|
|
||||||
shareContainer.leadingAnchor.constraint(equalTo: topControlsView.leadingAnchor),
|
|
||||||
shareContainer.bottomAnchor.constraint(equalTo: topControlsView.bottomAnchor),
|
|
||||||
shareContainer.widthAnchor.constraint(greaterThanOrEqualToConstant: 50),
|
|
||||||
shareContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: 50),
|
|
||||||
|
|
||||||
shareImage.centerXAnchor.constraint(equalTo: shareContainer.centerXAnchor),
|
|
||||||
shareImage.centerYAnchor.constraint(equalTo: shareContainer.centerYAnchor),
|
|
||||||
shareButtonTopConstraint,
|
|
||||||
shareButtonLeadingConstraint,
|
|
||||||
|
|
||||||
shareImage.widthAnchor.constraint(equalToConstant: 24),
|
|
||||||
shareImage.heightAnchor.constraint(equalToConstant: 24),
|
|
||||||
])
|
|
||||||
|
|
||||||
let closeContainer = UIView()
|
|
||||||
closeContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(closeButtonPressed)))
|
|
||||||
closeContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
topControlsView.addSubview(closeContainer)
|
|
||||||
let closeImage = UIImageView(image: UIImage(systemName: "xmark"))
|
|
||||||
closeImage.tintColor = .white
|
|
||||||
closeImage.contentMode = .scaleAspectFit
|
|
||||||
closeImage.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
closeContainer.addSubview(closeImage)
|
|
||||||
closeButtonTopConstraint = closeImage.topAnchor.constraint(greaterThanOrEqualTo: closeContainer.topAnchor)
|
|
||||||
closeButtonTrailingConstraint = closeContainer.trailingAnchor.constraint(greaterThanOrEqualTo: closeImage.trailingAnchor)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
closeContainer.topAnchor.constraint(equalTo: topControlsView.topAnchor),
|
|
||||||
closeContainer.trailingAnchor.constraint(equalTo: topControlsView.trailingAnchor),
|
|
||||||
closeContainer.bottomAnchor.constraint(equalTo: closeContainer.bottomAnchor),
|
|
||||||
closeContainer.widthAnchor.constraint(greaterThanOrEqualToConstant: 50),
|
|
||||||
closeContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: 50),
|
|
||||||
|
|
||||||
closeImage.centerXAnchor.constraint(equalTo: closeContainer.centerXAnchor),
|
|
||||||
closeImage.centerYAnchor.constraint(equalTo: closeContainer.centerYAnchor),
|
|
||||||
closeButtonTopConstraint,
|
|
||||||
closeButtonTrailingConstraint,
|
|
||||||
|
|
||||||
closeImage.widthAnchor.constraint(equalToConstant: 24),
|
|
||||||
closeImage.heightAnchor.constraint(equalToConstant: 24),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLayoutSubviews() {
|
override func viewDidLayoutSubviews() {
|
||||||
super.viewDidLayoutSubviews()
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
|
@ -215,7 +156,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
// since the corner radius didn't change
|
// since the corner radius didn't change
|
||||||
let notchWidth: CGFloat = 210
|
let notchWidth: CGFloat = 210
|
||||||
let earWidth = (view.bounds.width - notchWidth) / 2
|
let earWidth = (view.bounds.width - notchWidth) / 2
|
||||||
let offset = (earWidth - shareImage.bounds.width) / 2
|
let offset = (earWidth - shareButton.bounds.width) / 2
|
||||||
shareButtonLeadingConstraint.constant = offset
|
shareButtonLeadingConstraint.constant = offset
|
||||||
closeButtonTrailingConstraint.constant = offset
|
closeButtonTrailingConstraint.constant = offset
|
||||||
} else if pillDeviceTopInsets.contains(view.safeAreaInsets.top) {
|
} else if pillDeviceTopInsets.contains(view.safeAreaInsets.top) {
|
||||||
|
@ -334,7 +275,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
|
|
||||||
@IBAction func sharePressed(_ sender: Any) {
|
@IBAction func sharePressed(_ sender: Any) {
|
||||||
let activityVC = UIActivityViewController(activityItems: contentView.activityItemsForSharing, applicationActivities: nil)
|
let activityVC = UIActivityViewController(activityItems: contentView.activityItemsForSharing, applicationActivities: nil)
|
||||||
activityVC.popoverPresentationController?.sourceView = shareImage
|
activityVC.popoverPresentationController?.sourceView = shareButton
|
||||||
present(activityVC, animated: true)
|
present(activityVC, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,15 @@
|
||||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="LargeImageViewController" customModule="Tusker" customModuleProvider="target">
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="LargeImageViewController" customModule="Tusker" customModuleProvider="target">
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="bottomControlsView" destination="rPa-Zu-T6g" id="Rgz-AQ-9nt"/>
|
<outlet property="bottomControlsView" destination="rPa-Zu-T6g" id="Rgz-AQ-9nt"/>
|
||||||
|
<outlet property="closeButton" destination="pnA-ne-k0v" id="RPP-cB-9ap"/>
|
||||||
|
<outlet property="closeButtonTopConstraint" destination="ImD-2H-0XK" id="DUe-b1-a2N"/>
|
||||||
|
<outlet property="closeButtonTrailingConstraint" destination="JFe-ig-3Ic" id="cWO-Rr-y3F"/>
|
||||||
<outlet property="descriptionLabel" destination="eo5-fc-RV8" id="vrW-RJ-y5k"/>
|
<outlet property="descriptionLabel" destination="eo5-fc-RV8" id="vrW-RJ-y5k"/>
|
||||||
<outlet property="scrollView" destination="Skj-xq-AgQ" id="TFb-zF-m1b"/>
|
<outlet property="scrollView" destination="Skj-xq-AgQ" id="TFb-zF-m1b"/>
|
||||||
|
<outlet property="shareButton" destination="vhp-0u-Q0S" id="JZS-K9-4w9"/>
|
||||||
|
<outlet property="shareButtonLeadingConstraint" destination="MJx-2r-p0k" id="Dn5-Eg-Pid"/>
|
||||||
|
<outlet property="shareButtonTopConstraint" destination="sgG-dC-xXP" id="Rjp-od-00F"/>
|
||||||
|
<outlet property="topControlsHeightConstraint" destination="6XT-D6-8FS" id="mTB-LF-50H"/>
|
||||||
<outlet property="topControlsView" destination="kHo-B9-R7a" id="8sJ-xQ-7ix"/>
|
<outlet property="topControlsView" destination="kHo-B9-R7a" id="8sJ-xQ-7ix"/>
|
||||||
<outlet property="view" destination="BJw-5C-9nT" id="1C2-VA-mNf"/>
|
<outlet property="view" destination="BJw-5C-9nT" id="1C2-VA-mNf"/>
|
||||||
</connections>
|
</connections>
|
||||||
|
@ -26,8 +33,45 @@
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<gestureRecognizers/>
|
<gestureRecognizers/>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kHo-B9-R7a">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kHo-B9-R7a">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="36"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="36"/>
|
||||||
|
<subviews>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vhp-0u-Q0S">
|
||||||
|
<rect key="frame" x="16" y="16" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="4tF-oL-qXT"/>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="zWx-jJ-dBj"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<state key="normal" image="square.and.arrow.up" catalog="system"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="sharePressed:" destination="-1" eventType="touchUpInside" id="7Oz-zv-m2t"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pnA-ne-k0v">
|
||||||
|
<rect key="frame" x="339" y="16" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="eg0-hN-rda"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="fmA-pI-8WB"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<state key="normal" image="xmark" catalog="system"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="closeButtonPressed:" destination="-1" eventType="touchUpInside" id="7o3-ET-EMo"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" secondItem="pnA-ne-k0v" secondAttribute="height" constant="16" id="6XT-D6-8FS"/>
|
||||||
|
<constraint firstItem="pnA-ne-k0v" firstAttribute="top" secondItem="kHo-B9-R7a" secondAttribute="top" constant="16" id="ImD-2H-0XK"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="pnA-ne-k0v" secondAttribute="trailing" constant="16" id="JFe-ig-3Ic"/>
|
||||||
|
<constraint firstItem="vhp-0u-Q0S" firstAttribute="leading" secondItem="kHo-B9-R7a" secondAttribute="leading" constant="16" id="MJx-2r-p0k"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="vhp-0u-Q0S" secondAttribute="bottom" id="fi6-JS-UmZ"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="pnA-ne-k0v" secondAttribute="bottom" id="hEU-VY-WTd"/>
|
||||||
|
<constraint firstItem="vhp-0u-Q0S" firstAttribute="top" secondItem="kHo-B9-R7a" secondAttribute="top" constant="16" id="sgG-dC-xXP"/>
|
||||||
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rPa-Zu-T6g">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rPa-Zu-T6g">
|
||||||
<rect key="frame" x="0.0" y="622.5" width="375" height="44.5"/>
|
<rect key="frame" x="0.0" y="622.5" width="375" height="44.5"/>
|
||||||
|
@ -66,4 +110,8 @@
|
||||||
<point key="canvasLocation" x="-164" y="476"/>
|
<point key="canvasLocation" x="-164" y="476"/>
|
||||||
</view>
|
</view>
|
||||||
</objects>
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="square.and.arrow.up" catalog="system" width="115" height="128"/>
|
||||||
|
<image name="xmark" catalog="system" width="128" height="113"/>
|
||||||
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -246,9 +246,17 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
snapshot.insertItems(presentItems.map { .status(id: $0, state: .unknown) }, beforeItem: .gap)
|
snapshot.insertItems(presentItems.map { .status(id: $0, state: .unknown) }, beforeItem: .gap)
|
||||||
|
|
||||||
if applySnapshotBeforeScrolling {
|
if applySnapshotBeforeScrolling {
|
||||||
let firstVisibleIndexPath = collectionView.indexPathsForVisibleItems.min()!
|
// use a snapshot of the collection view to hide the flicker as the content offset changes and then changes back
|
||||||
let firstVisibleItem = dataSource.itemIdentifier(for: firstVisibleIndexPath)!
|
let snapshotView = collectionView.snapshotView(afterScreenUpdates: false)!
|
||||||
applySnapshot(snapshot, maintainingBottomRelativeScrollPositionOf: firstVisibleItem)
|
snapshotView.layer.zPosition = 1000
|
||||||
|
snapshotView.frame = view.bounds
|
||||||
|
view.addSubview(snapshotView)
|
||||||
|
|
||||||
|
let bottomOffset = collectionView.contentSize.height - collectionView.contentOffset.y
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: false) {
|
||||||
|
self.collectionView.contentOffset = CGPoint(x: 0, y: self.collectionView.contentSize.height - bottomOffset)
|
||||||
|
snapshotView.removeFromSuperview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = ToastConfiguration(title: "Jump to present")
|
var config = ToastConfiguration(title: "Jump to present")
|
||||||
|
@ -268,50 +276,6 @@ 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
|
|
||||||
}
|
|
||||||
|
|
||||||
dataSource.apply(snapshot, animatingDifferences: false) {
|
|
||||||
if let indexPathOfItemAfterOriginalGap = 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 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 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
cur = IndexPath(row: cur.row - 1, section: cur.section)
|
|
||||||
self.collectionView.scrollToItem(at: cur, at: .top, animated: false)
|
|
||||||
self.collectionView.layoutIfNeeded()
|
|
||||||
let attrs = self.collectionView.layoutAttributesForItem(at: cur)!
|
|
||||||
amountScrolledUp += attrs.size.height
|
|
||||||
}
|
|
||||||
self.collectionView.contentOffset.y += amountScrolledUp
|
|
||||||
self.collectionView.contentOffset.y -= firstItemAfterOriginalGapOffsetFromTop
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshotView.removeFromSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimelineViewController {
|
extension TimelineViewController {
|
||||||
|
@ -575,8 +539,29 @@ extension TimelineViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if addedItems {
|
if addedItems {
|
||||||
let firstItemAfterOriginalGap = statusItems[gapIndex + 1]
|
// use a snapshot of the collection view to hide the flicker as the content offset changes and then changes back
|
||||||
applySnapshot(snapshot, maintainingBottomRelativeScrollPositionOf: firstItemAfterOriginalGap)
|
let snapshotView = collectionView.snapshotView(afterScreenUpdates: false)!
|
||||||
|
snapshotView.layer.zPosition = 1000
|
||||||
|
snapshotView.frame = view.bounds
|
||||||
|
view.addSubview(snapshotView)
|
||||||
|
|
||||||
|
// Yes, these are all load bearing. Setting the contentOffset seems to cause the collection view to recalculate the size
|
||||||
|
// of some cells, thus changing the contentSize and the offset necessary to match to match the bottom offset.
|
||||||
|
// Three DispatchQueue.main.async's seems to be the fewest we can reliably get away with.
|
||||||
|
let bottomOffset = collectionView.contentSize.height - collectionView.contentOffset.y
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: false) {
|
||||||
|
self.collectionView.contentOffset = CGPoint(x: 0, y: self.collectionView.contentSize.height - bottomOffset)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.collectionView.contentOffset = CGPoint(x: 0, y: self.collectionView.contentSize.height - bottomOffset)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.collectionView.contentOffset = CGPoint(x: 0, y: self.collectionView.contentSize.height - bottomOffset)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.collectionView.contentOffset = CGPoint(x: 0, y: self.collectionView.contentSize.height - bottomOffset)
|
||||||
|
snapshotView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dataSource.apply(snapshot, animatingDifferences: true) {}
|
dataSource.apply(snapshot, animatingDifferences: true) {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue