ios - 在 swift 中拖放 collectionViewCell

标签 ios swift uicollectionview drag-and-drop

每当我拖放其他部分中的单元格时,应用程序都会崩溃,当我拖放同一部分中的单元格时,它会很好地工作,任何解决它的想法都会提前非常感谢。以下是完整代码。

//错误:由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“尝试将项目 21 插入第 1 部分,但更新后只有 1 个部分”

// View Controller

import UIKit

class ViewController: UIViewController {
    var deviceName = ""
    let cellName = "IconCell". // cell name
    var pressActivated = false
    @IBOutlet weak var collectionView: UICollectionView!
    var nameArrays = [
        0: ["twitter", "facebook", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
        1: ["twitter", "twitter", "", "twitter", "", "", "", "", "", "", "", "twitter", "", "", "twitter", "twitter", "twitter", "twitter", "twitter", "twitter", "twitter", "twitter", "twitter", "twitter"]
    ]

    let statusBarTappedNotification = Notification(name: Notification.Name(rawValue: "statusBarTappedNotification"))

    @IBOutlet weak var pageController: UIPageControl!




    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setupView()

    }

    private func setupView(){
        self.collectionView.register(UINib.init(nibName: cellName, bundle: nil), forCellWithReuseIdentifier: cellName)
        pageController.numberOfPages = nameArrays.keys.count
        pageController.currentPage = 0

        collectionView.dragDelegate = self
        collectionView.dropDelegate = self


       // collectionView.dragInteractionEnabled = true
        collectionView.reorderingCadence = .fast

        NotificationCenter.default.addObserver(forName: statusBarTappedNotification.name, object: .none, queue: .none) { _ in
            print("status bar tapped")
            self.pressActivated = false
            self.collectionView.reloadData()
        }
    }

    @objc private func longPress(){
        print("long Pressed")
        pressActivated = true
        if pressActivated{
            collectionView.dragInteractionEnabled = true
        }
        collectionView.reloadData()
    }



    private func copyItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView)
    {
        collectionView.performBatchUpdates({
            var indexPaths = [IndexPath]()
            for (index, item) in coordinator.items.enumerated()
            {
                let indexPath = IndexPath(row: destinationIndexPath.item + index, section: destinationIndexPath.section)

                print(indexPath.section)
                 print(indexPath.item)

                let key  = self.nameArrays.keys.sorted()[indexPath.section]

                self.nameArrays[key]!.insert(item.dragItem.localObject as! String, at: indexPath.item)
//
               indexPaths.append(indexPath)
            }
            collectionView.insertItems(at: indexPaths)
        })
    }

    private func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView)
    {
        let items = coordinator.items
        if items.count == 1, let item = items.first, let sourceIndexPath = item.sourceIndexPath
        {
            var dIndexPath = destinationIndexPath
            if dIndexPath.row >= collectionView.numberOfItems(inSection: 0)
            {
                dIndexPath.row = collectionView.numberOfItems(inSection: 0) - 1
            }
            collectionView.performBatchUpdates({


               self.nameArrays.removeValue(forKey: sourceIndexPath.item)


                collectionView.deleteItems(at: [sourceIndexPath])
                collectionView.insertItems(at: [dIndexPath])
                print(dIndexPath) // till here it works good
            })
            coordinator.drop(items.first!.dragItem, toItemAt: dIndexPath)
        }
    }
}

extension ViewController: UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {


    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        for cell in collectionView.visibleCells {
            let indexPath = collectionView.indexPath(for: cell)
            if let indexPath = indexPath {
                print("\(indexPath)")
                self.pageController.currentPage = indexPath.section
            }
        }
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return nameArrays.keys.count. // number of sections in collection view
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let key = nameArrays.keys.sorted()[section]
        return nameArrays[key]!.count // number of rows in collection View
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell: IconCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellName, for: indexPath) as! IconCell
        // setting data to cells
        let holdGesture = UILongPressGestureRecognizer()
        holdGesture.addTarget(self, action: #selector(longPress))
        holdGesture.minimumPressDuration = 0.5
        cell.addGestureRecognizer(holdGesture)

        cell.backgroundColor = .clear
        let key = nameArrays.keys.sorted()[indexPath.section]
        cell.appName.text = nameArrays[key]![indexPath.item]
        cell.appIcon.image = UIImage.init(named: nameArrays[key]![indexPath.item])
        if pressActivated{
            let transformAnim  = CAKeyframeAnimation(keyPath:"transform")
            transformAnim.values  = [NSValue(caTransform3D: CATransform3DMakeRotation(0.04, 0.0, 0.0, 1.0)),NSValue(caTransform3D: CATransform3DMakeRotation(-0.04 , 0, 0, 1))]
            transformAnim.autoreverses = true
            transformAnim.duration  = Double(indexPath.row).truncatingRemainder(dividingBy: 2) == 0 ?   0.115 : 0.105
            transformAnim.repeatCount = Float.infinity
            cell.layer.add(transformAnim, forKey: "transform")
            cell.closeButton.isHidden = false
            if cell.appName.text == ""{
                cell.closeButton.isHidden = true
            }
        }
        else {
            cell.closeButton.isHidden = true
        }
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.bounds.width/4 - 10, height: 80)
    }
}

class CustomCollectionViewFlowLayout: UICollectionViewFlowLayout
{
    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint
    {
        if let collectionViewBounds = self.collectionView?.bounds
        {
            let halfWidthOfVC = collectionViewBounds.size.width * 0.5
            let proposedContentOffsetCenterX = proposedContentOffset.x + halfWidthOfVC
            if let attributesForVisibleCells = self.layoutAttributesForElements(in: collectionViewBounds)
            {
                var candidateAttribute : UICollectionViewLayoutAttributes?
                for attributes in attributesForVisibleCells
                {
                    let candAttr : UICollectionViewLayoutAttributes? = candidateAttribute
                    if candAttr != nil
                    {
                        let a = attributes.center.x - proposedContentOffsetCenterX
                        let b = candAttr!.center.x - proposedContentOffsetCenterX
                        if abs(a) < abs(b)
                        {
                            candidateAttribute = attributes
                        }
                    }
                    else
                    {
                        candidateAttribute = attributes
                        continue
                    }
                }

                if candidateAttribute != nil
                {
                    return CGPoint(x: candidateAttribute!.center.x - halfWidthOfVC, y: proposedContentOffset.y);
                }
            }
        }
        return CGPoint.zero
    }
}

extension ViewController : UICollectionViewDragDelegate
{
    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem]
    {
        let key = nameArrays.keys.sorted()[indexPath.section]
        let item = self.nameArrays[key]![indexPath.item]
        let itemProvider = NSItemProvider(object: item as NSString)
        let dragItem = UIDragItem(itemProvider: itemProvider)
        dragItem.localObject = item
        return [dragItem]
    }

    func collectionView(_ collectionView: UICollectionView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem]
    {
        let key = nameArrays.keys.sorted()[indexPath.section]
        let item = self.nameArrays[key]![indexPath.item]
        let itemProvider = NSItemProvider(object: item as NSString)
        let dragItem = UIDragItem(itemProvider: itemProvider)
        dragItem.localObject = item
        return [dragItem]
    }

    func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters?
    {

        let previewParameters = UIDragPreviewParameters()
        previewParameters.visiblePath = UIBezierPath(rect: CGRect(x: 25, y: 25, width: 120, height: 120))
        return previewParameters

    }
}

// MARK: - UICollectionViewDropDelegate Methods
extension ViewController : UICollectionViewDropDelegate
{
    func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool
    {
        return session.canLoadObjects(ofClass: NSString.self)
    }

    func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal
    {

        if collectionView.hasActiveDrag
        {
            return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
        }
        else
        {
            return UICollectionViewDropProposal(operation: .forbidden)
        }

    }

    func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator)
    {
        let destinationIndexPath: IndexPath
        if let indexPath = coordinator.destinationIndexPath
        {
            destinationIndexPath = indexPath
        }
        else
        {
            // Get last index path of table view.
            let section = collectionView.numberOfSections - 1
            let row = collectionView.numberOfItems(inSection: section)
            destinationIndexPath = IndexPath(row: row, section: section)
        }

        switch coordinator.proposal.operation
        {
        case .move:
             print("move")
            self.reorderItems(coordinator: coordinator, destinationIndexPath:destinationIndexPath, collectionView: collectionView)
            break

        case .copy:
            print("copy")
            self.copyItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)

        default:
            return
        }
    }
}

最佳答案

您必须使用beginInteractiveMovementForItemAtIndexPath:endInteractiveMovement

请参阅 UICollectionView reference 中的以交互方式重新排序项目 部分

关于ios - 在 swift 中拖放 collectionViewCell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56672496/

相关文章:

ios - 在 Swift 中设置 UIDatePicker 的文本颜色

swift - NSFetchRequest as entityName 给出错误 "use of undeclared type"

ios - accessibilityElementDidBecomeFocused 没有被调用

ios - 删除行后从核心数据中保存和获取 Collection View 单元格

ios - 将 FirebaseUI-IOS 与自定义 UICollectionViewCell 一起使用时出错

ios - 尝试将 UIImageView 设置为 View

iphone - 如何从导航堆栈中删除所有 View ?

ios - UICollectionViewCell 自动调整大小?

ios - UICollectionView - 如果在更新前不可见,将要删除的单元格不会设置动画

ios - 无法让我的标题在 swift 4 中显示在我的 UIcollectionView 页面上