UICollectionView快速拖放

时间:2019-10-11 06:19:33

标签: ios swift xcode uicollectionview

我正在使用UICollectionView快速创建一个项目,我需要对每个框执行拖放操作并交换它们的位置。每个盒子都有不同的数据和不同的大小,因此用户需要操纵其布局,为此我需要使用户能够拖放这些视图。我写了一个代码,但是当我开始拖放时,事件将传输其数据,而不是框本身。我该如何解决?

    import UIKit

    class ViewController: UIViewController {

        var cellIds = ["1","2","3"]

            @IBOutlet weak var collectionView: UICollectionView!
          fileprivate var longPressGesture =  UILongPressGestureRecognizer();

        let cellSizes = [
            CGSize(width:190, height:200),
             CGSize(width:190, height:200),
              CGSize(width:190, height:200),

        ]

        @objc func longTap(_ gesture: UIGestureRecognizer){

            switch(gesture.state) {
            case .began:
                guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
                    return
                }
                collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
            case .changed:
                collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
            case .ended:
                collectionView.endInteractiveMovement()
                //doneBtn.isHidden = false
                //longPressedEnabled = true
                self.collectionView.reloadData()
            default:
                collectionView.cancelInteractiveMovement()
            }
        }

        override func viewDidLoad() {
            super.viewDidLoad()


            longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.longTap(_:)))
            collectionView.addGestureRecognizer(longPressGesture)


            collectionView!.register(UICustomCollectionViewCell.self, forCellWithReuseIdentifier: "MenuCell")

            self.automaticallyAdjustsScrollViewInsets = false
        }

        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
            //top, left, bottom, right
            return UIEdgeInsets(top: 130, left: 1, bottom: 0, right: 10)
        }

    }

    extension ViewController: UICollectionViewDelegate {
        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
           // print("User tapped on \(cellIds[indexPath.row])")
        }

        func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
            return true
        }


         func userDidEndDragging(_ cell: UICollectionViewCell?) {


         }




        func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
            let item = cellIds.remove(at: sourceIndexPath.item)
            cellIds.insert(item, at: destinationIndexPath.item)
            print(cellIds);
        }
     }



    extension ViewController: UICollectionViewDataSource {
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return cellIds.count
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: indexPath) as? UICustomCollectionViewCell

            cell!.layer.cornerRadius=10
            cell!.layer.masksToBounds=true

             var id = cellIds[indexPath.row];

            if (id == "1")
            {

              cell?.lblTest1.text = "amsterdam";
                cell?.lblTest1.tag = 100
            }
            if (id == "2")
            {
                  cell?.lblTest2.text = "madrid";
                  cell?.lblTest2.tag = 101

            }

            if (id == "3")
            {
                 cell?.lblTest3.text = "istanbul";
                 cell?.lblTest3.tag = 102
            }

            return cell ?? UICollectionViewCell()

         }
    }

    extension ViewController: UICollectionViewDelegateFlowLayout {
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return cellSizes[indexPath.item]
        }
    }

import UIKit
class UICustomCollectionViewCell: UICollectionViewCell {

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func prepareForReuse() {
    super.prepareForReuse()
}

override init(frame: CGRect) {
    super.init(frame: frame)
       self.addSubview(lblTest1)
      self.addSubview(lblTest2)
      self.addSubview(lblTest3)
}

var lblTest1:UILabel = {
    let label1 = UILabel(frame: CGRect(x:10, y: 20, width: 90 , height: 40))
    label1.lineBreakMode = .byWordWrapping
    label1.numberOfLines = 0
    label1.tag = 100;
    label1.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
    return label1
}()


var lblTest2:UILabel = {
    let label2 = UILabel(frame: CGRect(x:10, y: 20, width: 90 , height: 40))
    label2.lineBreakMode = .byWordWrapping
    label2.numberOfLines = 0
    label2.tag = 101;
    label2.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
    return label2
}()


var lblTest3:UILabel = {
    let label3 = UILabel(frame: CGRect(x:10, y: 20, width: 90 , height: 40))
    label3.lineBreakMode = .byWordWrapping
    label3.numberOfLines = 0
    label3.tag = 102;
    label3.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
    return label3
}()

override func layoutSubviews() {
    super.layoutSubviews()
}
}

1 个答案:

答案 0 :(得分:0)

这是我实现优雅的拖放collectionView的方式:

  • 我的班级所在的集合视图
class FavoritesVC: UIViewController {

    //MARK: IBOutlets
    @IBOutlet weak var collectionView: UICollectionView!

    //MARK: View Model
    let viewModel = FavoritesViewModel()

    //MARK: Properties
    lazy var dragDropFlowLayout: UICollectionViewLayout = {
        let flow = DragDropFlowLayout()
        flow.scrollDirection = .vertical
        flow.headerReferenceSize = CGSize(width: collectionView.frame.width, height: 0.5)
        return flow
    }()

    //MARK: Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()

        setupCollectionView()
        viewModel.competitionsUpdateCallback = {
            self.collectionView.reloadData()
        }
    }

    //MARK: Private functions
    private func setupCollectionView() {

        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.collectionViewLayout = dragDropFlowLayout
    }
}

//MARK: CollectionView DataSource
extension FavoritesVC: UICollectionViewDataSource {

    ///...methods for the collectionViewDatasource which aren't relevant here

    func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {

        return true
    }

    func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destination: IndexPath) {

        //save the new order of the objects in the data
        ...
    }
}

//MARK: CollectionView Flow Layout Delegate
extension FavoritesVC: UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let size = CGSize(width: collectionView.frame.width / 2 - 15, height: 150)
        return size
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {

        return 10
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {

        return 10
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

        return UIEdgeInsets(top: 20, left: 10, bottom: 20, right: 10)
    }
}
  • 拖放自定义流程布局
class DragDropFlowLayout: UICollectionViewFlowLayout {

    var longPress: UILongPressGestureRecognizer!
    var originalIndexPath: IndexPath?
    var draggingIndexPath: IndexPath?
    var draggingView: UIView?
    var dragOffset = CGPoint.zero

    override func prepare() {
        super.prepare()

        installGestureRecognizer()
    }

    func applyDraggingAttributes(attributes: UICollectionViewLayoutAttributes) {
        attributes.alpha = 0
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)
        attributes?.forEach { a in
            if a.indexPath == draggingIndexPath {
                if a.representedElementCategory == .cell {
                    self.applyDraggingAttributes(attributes: a)
                }
            }
        }
        return attributes
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.layoutAttributesForItem(at: indexPath)
        if let attributes = attributes, indexPath == draggingIndexPath {
            if attributes.representedElementCategory == .cell {
                applyDraggingAttributes(attributes: attributes)
            }
        }
        return attributes
    }

    func installGestureRecognizer() {
        if longPress == nil {
            longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
            longPress.minimumPressDuration = 0.2
            collectionView?.addGestureRecognizer(longPress)
        }
    }

    @objc func handleLongPress(_ longPress: UILongPressGestureRecognizer) {
        let location = longPress.location(in: collectionView!)
        switch longPress.state {
        case .began: startDragAtLocation(location: location)
        case .changed: updateDragAtLocation(location: location)
        case .ended: endDragAtLocation(location: location)
        default:
            break
        }
    }

    func startDragAtLocation(location: CGPoint) {
        guard let cv = collectionView else { return }
        guard let indexPath = cv.indexPathForItem(at: location) else { return }
        guard cv.dataSource?.collectionView?(cv, canMoveItemAt: indexPath) == true else { return }
        guard let cell = cv.cellForItem(at: indexPath) else { return }

        originalIndexPath = indexPath
        draggingIndexPath = indexPath
        draggingView = cell.snapshotView(afterScreenUpdates: true)
        draggingView!.frame = cell.frame
        cv.addSubview(draggingView!)

        dragOffset = CGPoint(x: draggingView!.center.x - location.x, y: draggingView!.center.y - location.y)

        draggingView?.layer.shadowPath = UIBezierPath(rect: draggingView!.bounds).cgPath
        draggingView?.layer.shadowColor = UIColor.black.cgColor
        draggingView?.layer.shadowOpacity = 0.8
        draggingView?.layer.shadowRadius = 10

        invalidateLayout()

        UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.55, initialSpringVelocity: 0, options: [], animations: {
            self.draggingView?.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
        }, completion: nil)
    }

    func updateDragAtLocation(location: CGPoint) {
        guard let view = draggingView else { return }
        guard let cv = collectionView else { return }

        view.center = CGPoint(x: location.x + dragOffset.x, y: location.y + dragOffset.y)

        if let newIndexPath = cv.indexPathForItem(at: location) {
            cv.moveItem(at: draggingIndexPath!, to: newIndexPath)
            draggingIndexPath = newIndexPath
        }
    }

    func endDragAtLocation(location: CGPoint) {
        guard let dragView = draggingView else { return }
        guard let indexPath = draggingIndexPath else { return }
        guard let cv = collectionView else { return }
        guard let datasource = cv.dataSource else { return }

        let targetCenter = datasource.collectionView(cv, cellForItemAt: indexPath).center

        UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.55, initialSpringVelocity: 0, options: [], animations: {
            dragView.center = targetCenter
            dragView.transform = .identity
            dragView.layer.shadowColor = UIColor.white.cgColor
            dragView.layer.shadowOpacity = 0
            dragView.layer.shadowRadius = 0

        }) { (completed) in

            if indexPath != self.originalIndexPath! {
                datasource.collectionView?(cv, moveItemAt: self.originalIndexPath!, to: indexPath)
            }

            dragView.removeFromSuperview()
            self.draggingIndexPath = nil
            self.draggingView = nil
            self.invalidateLayout()
        }
    }
}