自定义拖放UICollectionViewLayout捕捉到网格

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义拖放UICollectionViewLayout捕捉到网格相关的知识,希望对你有一定的参考价值。

我正在开发一个应用程序,我可以跟踪我的青少年足球队的替换。

按照Payal Gupta关于拖放到集合和表格的教程,我设法在两个集合视图(PlayersOntoTheField和Substitutes)之间进行拖放工作Screenshot

当我将替代玩家拖入我的游乐场时,它现在应该捕捉到预定义的团队阵容(例如截屏中的3-2-1)。是否可以使用自定义UICollectionViewLayout获取此类行为,或者是否有人有其他建议?

非常感谢您提前寻求帮助。

丹尼

//Based on a work by: 
//Payal Gupta (https://github.com/pgpt10/DragAndDrop-CollectionView)

import UIKit

class ViewController: UIViewController
{
    private var substitutes = ["player1", "player2", "player3", "player4"]
    private var players = [String]()

    @IBOutlet weak var substitutesCollectionView: UICollectionView!
    @IBOutlet weak var playersCollectionView: UICollectionView!

    override func viewDidLoad()
    {
        super.viewDidLoad()

        //SubstitutesCollectionView drag and drop configuration
        self.substitutesCollectionView.dragInteractionEnabled = true
        self.substitutesCollectionView.dragDelegate = self
        self.substitutesCollectionView.dropDelegate = self

        //PlayersCollectionView drag and drop configuration
        self.playersCollectionView.dragInteractionEnabled = true
        self.playersCollectionView.dropDelegate = self
        self.playersCollectionView.dragDelegate = self
        self.playersCollectionView.reorderingCadence = .fast //default value - .immediate
    }

    //MARK: Private Methods
    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({
                if collectionView === self.playersCollectionView
                {
                    self.players.remove(at: sourceIndexPath.row)
                    self.players.insert(item.dragItem.localObject as! String, at: dIndexPath.row)
                }
                else
                {
                    self.substitutes.remove(at: sourceIndexPath.row)
                    self.substitutes.insert(item.dragItem.localObject as! String, at: dIndexPath.row)
                }
                collectionView.deleteItems(at: [sourceIndexPath])
                collectionView.insertItems(at: [dIndexPath])
            })
            coordinator.drop(items.first!.dragItem, toItemAt: dIndexPath)
        }
    }

    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.row + index, section: destinationIndexPath.section)
                if collectionView === self.playersCollectionView
                {
                    self.players.insert(item.dragItem.localObject as! String, at: indexPath.row)
                }
                else
                {
                    self.substitutes.insert(item.dragItem.localObject as! String, at: indexPath.row)
                }
                indexPaths.append(indexPath)
            }
            collectionView.insertItems(at: indexPaths)
        })
    }
}

// MARK: - UICollectionViewDataSource Methods
extension ViewController : UICollectionViewDataSource
{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        return collectionView == self.substitutesCollectionView ? self.substitutes.count : self.players.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    {
        if collectionView == self.substitutesCollectionView
        {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath) as! MyCollectionViewCell
            cell.customImageView?.image = UIImage(named: self.substitutes[indexPath.row])
            cell.customLabel.text = self.substitutes[indexPath.row].capitalized
            return cell
        }
        else
        {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell2", for: indexPath) as! MyCollectionViewCell
            cell.customImageView?.image = UIImage(named: self.players[indexPath.row])
            cell.customLabel.text = self.players[indexPath.row].capitalized
            return cell
        }
    }
}

// MARK: - UICollectionViewDragDelegate Methods
extension ViewController : UICollectionViewDragDelegate
{
    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem]
    {
        let item = collectionView == substitutesCollectionView ? self.substitutes[indexPath.row] : self.players[indexPath.row]
        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 item = collectionView == substitutesCollectionView ? self.substitutes[indexPath.row] : self.players[indexPath.row]
        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?
    {
        if collectionView == substitutesCollectionView
        {
            let previewParameters = UIDragPreviewParameters()
            previewParameters.visiblePath = UIBezierPath(rect: CGRect(x: 15, y: 5, width: 30, height: 30))
            return previewParameters
        }
        return nil
    }
}

// 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 === self.substitutesCollectionView
        {
            if collectionView.hasActiveDrag
            {
                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
            }
            else
            {
                return UICollectionViewDropProposal(operation: .forbidden)
            }
        }
        else
        {
            if collectionView.hasActiveDrag
            {
                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
            }
            else
            {
                return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)
            }
        }
    }

    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:
            self.reorderItems(coordinator: coordinator, destinationIndexPath:destinationIndexPath, collectionView: collectionView)
            break

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

        default:
            return
        }
    }
}
答案

到目前为止我的自定义UICollectionViewLayout的解决方法:

import UIKit

class LineUp_3_2_1View: UICollectionViewLayout {

    private var center: CGPoint!
    private var itemSize: CGSize!
    private var radiusOfCircleViews: CGFloat!
    private var numberOfItems: Int!

    override func prepare() {
        super.prepare()

        guard let collectionView = collectionView else { return }

        radiusOfCircleViews = CGFloat(30.0)
        itemSize = CGSize(width: radiusOfCircleViews * 2, height: radiusOfCircleViews * 2)
        center = CGPoint(x: collectionView.bounds.midX, y: collectionView.bounds.midY)
        numberOfItems = collectionView.numberOfItems(inSection: 0)
    }

    override var collectionViewContentSize: CGSize {
        return collectionView!.bounds.size
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)

        if (indexPath.item == 0) {attributes.center = CGPoint(x: 169, y: 344)}
        if (indexPath.item == 1) {attributes.center = CGPoint(x: 46, y: 250)}
        if (indexPath.item == 2) {attributes.center = CGPoint(x: 169, y: 250)}
        if (indexPath.item == 3) {attributes.center = CGPoint(x: 287, y: 250)}
        if (indexPath.item == 4) {attributes.center = CGPoint(x: 80, y: 156)}
        if (indexPath.item == 5) {attributes.center = CGPoint(x: 253, y: 156)}
        if (indexPath.item == 6) {attributes.center = CGPoint(x: 169, y: 62)}

        attributes.size = itemSize

        return attributes
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return (0 ..< collectionView!.numberOfItems(inSection: 0))
            .flatMap { item -> UICollectionViewLayoutAttributes? in    // `compactMap` in Xcode 9.3
                self.layoutAttributesForItem(at: IndexPath(item: item, section: 0))
        }
    }
}

以上是关于自定义拖放UICollectionViewLayout捕捉到网格的主要内容,如果未能解决你的问题,请参考以下文章

JPanel 中的 Java 自定义拖放

如何使用自定义项目小部件拖放 QListWidget 项目?

iOS 11拖放自定义文件?

Qt学习之路(54): 自定义拖放数据对象

在 QListWidget 之间拖放自定义小部件项目

nstableview 拖放与自定义单元格视图