chore: update poll option reorder UX

This commit is contained in:
CMK 2021-09-17 19:30:14 +08:00
parent b65e591a96
commit 75f1a4852a
1 changed files with 77 additions and 59 deletions

View File

@ -5,6 +5,7 @@
// Created by MainasuK Cirno on 2021-6-29. // Created by MainasuK Cirno on 2021-6-29.
// //
import os.log
import UIKit import UIKit
protocol ComposeStatusPollTableViewCellDelegate: AnyObject { protocol ComposeStatusPollTableViewCellDelegate: AnyObject {
@ -12,6 +13,8 @@ protocol ComposeStatusPollTableViewCellDelegate: AnyObject {
} }
final class ComposeStatusPollTableViewCell: UITableViewCell { final class ComposeStatusPollTableViewCell: UITableViewCell {
let logger = Logger(subsystem: "ComposeStatusPollTableViewCell", category: "UI")
private(set) var dataSource: UICollectionViewDiffableDataSource<ComposeStatusPollSection, ComposeStatusPollItem>! private(set) var dataSource: UICollectionViewDiffableDataSource<ComposeStatusPollSection, ComposeStatusPollItem>!
var observations = Set<NSKeyValueObservation>() var observations = Set<NSKeyValueObservation>()
@ -43,6 +46,7 @@ final class ComposeStatusPollTableViewCell: UITableViewCell {
collectionView.backgroundColor = .clear collectionView.backgroundColor = .clear
collectionView.alwaysBounceVertical = true collectionView.alwaysBounceVertical = true
collectionView.isScrollEnabled = false collectionView.isScrollEnabled = false
collectionView.dragInteractionEnabled = true
return collectionView return collectionView
}() }()
@ -75,12 +79,8 @@ extension ComposeStatusPollTableViewCell {
collectionViewHeightLayoutConstraint, collectionViewHeightLayoutConstraint,
]) ])
let longPressReorderGesture = UILongPressGestureRecognizer(target: self, action: #selector(ComposeStatusPollTableViewCell.longPressReorderGestureHandler(_:)))
collectionView.addGestureRecognizer(longPressReorderGesture)
collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
guard let self = self else { return } guard let self = self else { return }
print(collectionView.contentSize)
self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
} }
.store(in: &observations) .store(in: &observations)
@ -122,66 +122,84 @@ extension ComposeStatusPollTableViewCell {
return cell return cell
} }
} }
dataSource.reorderingHandlers.canReorderItem = { item in collectionView.dragDelegate = self
switch item { collectionView.dropDelegate = self
case .pollOption: return true
default: return false
}
}
// update reordered data source
dataSource.reorderingHandlers.didReorder = { [weak self] transaction in
guard let self = self else { return }
let items = transaction.finalSnapshot.itemIdentifiers
var pollOptionAttributes: [ComposeStatusPollItem.PollOptionAttribute] = []
for item in items {
guard case let .pollOption(attribute) = item else { continue }
pollOptionAttributes.append(attribute)
}
self.delegate?.composeStatusPollTableViewCell(self, pollOptionAttributesDidReorder: pollOptionAttributes)
}
} }
} }
extension ComposeStatusPollTableViewCell { // MARK: - UICollectionViewDragDelegate
extension ComposeStatusPollTableViewCell: UICollectionViewDragDelegate {
@objc private func longPressReorderGestureHandler(_ sender: UILongPressGestureRecognizer) {
switch(sender.state) { func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
case .began: guard let item = dataSource.itemIdentifier(for: indexPath) else { return [] }
guard let selectedIndexPath = collectionView.indexPathForItem(at: sender.location(in: collectionView)), switch item {
let cell = collectionView.cellForItem(at: selectedIndexPath) as? ComposeStatusPollOptionCollectionViewCell else { case .pollOption:
break let itemProvider = NSItemProvider(object: String(item.hashValue) as NSString)
} let dragItem = UIDragItem(itemProvider: itemProvider)
// check if pressing reorder bar no not dragItem.localObject = item
let locationInCell = sender.location(in: cell) return [dragItem]
guard cell.reorderBarImageView.frame.contains(locationInCell) else {
return
}
collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
case .changed:
guard let selectedIndexPath = collectionView.indexPathForItem(at: sender.location(in: collectionView)),
let dataSource = self.dataSource else {
break
}
guard let item = dataSource.itemIdentifier(for: selectedIndexPath),
case .pollOption = item else {
collectionView.cancelInteractiveMovement()
return
}
var position = sender.location(in: collectionView)
position.x = collectionView.frame.width * 0.5
collectionView.updateInteractiveMovementTargetPosition(position)
case .ended:
collectionView.endInteractiveMovement()
collectionView.reloadData()
default: default:
collectionView.cancelInteractiveMovement() return []
} }
} }
func collectionView(_ collectionView: UICollectionView, dragSessionIsRestrictedToDraggingApplication session: UIDragSession) -> Bool {
// drag to app should be the same app
return true
}
}
// MARK: - UICollectionViewDropDelegate
extension ComposeStatusPollTableViewCell: UICollectionViewDropDelegate {
// didUpdate
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
guard collectionView.hasActiveDrag,
let destinationIndexPath = destinationIndexPath,
let item = dataSource.itemIdentifier(for: destinationIndexPath)
else {
return UICollectionViewDropProposal(operation: .forbidden)
}
switch item {
case .pollOption:
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
default:
return UICollectionViewDropProposal(operation: .cancel)
}
}
// performDrop
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
guard let dropItem = coordinator.items.first,
let item = dropItem.dragItem.localObject as? ComposeStatusPollItem,
case .pollOption = item
else { return }
guard coordinator.proposal.operation == .move else { return }
guard let destinationIndexPath = coordinator.destinationIndexPath,
let _ = collectionView.cellForItem(at: destinationIndexPath) as? ComposeStatusPollOptionCollectionViewCell
else { return }
var snapshot = dataSource.snapshot()
guard destinationIndexPath.row < snapshot.itemIdentifiers.count else { return }
let anchorItem = snapshot.itemIdentifiers[destinationIndexPath.row]
snapshot.moveItem(item, afterItem: anchorItem)
dataSource.apply(snapshot)
coordinator.drop(dropItem.dragItem, toItemAt: destinationIndexPath)
}
}
extension ComposeStatusPollTableViewCell: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(originalIndexPath.debugDescription) -> \(proposedIndexPath.debugDescription)")
guard let _ = collectionView.cellForItem(at: proposedIndexPath) as? ComposeStatusPollOptionCollectionViewCell else {
return originalIndexPath
}
return proposedIndexPath
}
} }