chore: update poll option reorder UX
This commit is contained in:
parent
b65e591a96
commit
75f1a4852a
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue