From 02fd724b0bbf1397b0115ad666b32e8ba7e4c711 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 13 Sep 2024 11:20:10 -0400 Subject: [PATCH] Attachment reordering --- .../ComposeUI/Views/AttachmentsListView.swift | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListView.swift index a42d2c66..d01bf3a2 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListView.swift @@ -62,6 +62,11 @@ private struct Callbacks { draft.attachments = NSMutableOrderedSet(array: array) } + func reorderAttachments(with difference: CollectionDifference) { + let array = draft.draftAttachments.applying(difference)! + draft.attachments = NSMutableOrderedSet(array: array) + } + func addPhoto() { presentAssetPicker?() { insertAttachments(at: draft.attachments.count, itemProviders: $0.map(\.itemProvider)) @@ -100,6 +105,31 @@ private struct WrappedCollectionView: UIViewRepresentable { context.coordinator.dataSource = dataSource view.delegate = context.coordinator view.isScrollEnabled = false + + dataSource.reorderingHandlers.canReorderItem = { + if case .attachment(_) = $0 { + true + } else { + false + } + } + dataSource.reorderingHandlers.didReorder = { transaction in + let attachmentsChanges = transaction.difference.map { + switch $0 { + case .insert(let offset, let element, let associatedWith): + guard case .attachment(let attachment) = element else { fatalError() } + return CollectionDifference.Change.insert(offset: offset, element: attachment, associatedWith: associatedWith) + case .remove(let offset, let element, let associatedWith): + guard case .attachment(let attachment) = element else { fatalError() } + return CollectionDifference.Change.remove(offset: offset, element: attachment, associatedWith: associatedWith) + } + } + let attachmentsDiff = CollectionDifference(attachmentsChanges)! + callbacks.reorderAttachments(with: attachmentsDiff) + } + let longPressRecognizer = UILongPressGestureRecognizer(target: context.coordinator, action: #selector(WrappedCollectionViewCoordinator.reorderingLongPressRecognized)) + longPressRecognizer.delegate = context.coordinator + view.addGestureRecognizer(longPressRecognizer) return view } @@ -188,7 +218,7 @@ private final class IntrinsicContentSizeCollectionView: UICollectionView { } @available(iOS 16.0, *) -private final class WrappedCollectionViewCoordinator: NSObject, UICollectionViewDelegate { +private final class WrappedCollectionViewCoordinator: NSObject, UICollectionViewDelegate, UIGestureRecognizerDelegate { var callbacks: Callbacks var setHeightOfCellBeingDeleted: ((CGFloat) -> Void)? @@ -277,6 +307,41 @@ private final class WrappedCollectionViewCoordinator: NSObject, UICollectionView callbacks.togglePoll() } } + + func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveOfItemFromOriginalIndexPath originalIndexPath: IndexPath, atCurrentIndexPath currentIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath { + let snapshot = dataSource.snapshot() + let attachmentsSection = snapshot.indexOfSection(.attachments)! + if proposedIndexPath.section != attachmentsSection { + return IndexPath(item: snapshot.itemIdentifiers(inSection: .attachments).count - 1, section: attachmentsSection) + } else { + return proposedIndexPath + } + } + + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + let collectionView = gestureRecognizer.view as! UICollectionView + let location = gestureRecognizer.location(in: collectionView) + guard let indexPath = collectionView.indexPathForItem(at: location) else { + return false + } + return collectionView.beginInteractiveMovementForItem(at: indexPath) + } + + @objc func reorderingLongPressRecognized(_ recognizer: UILongPressGestureRecognizer) { + let collectionView = recognizer.view as! UICollectionView + switch recognizer.state { + case .began: + break + case .changed: + collectionView.updateInteractiveMovementTargetPosition(recognizer.location(in: collectionView)) + case .ended: + collectionView.endInteractiveMovement() + case .cancelled: + collectionView.cancelInteractiveMovement() + default: + break + } + } }