带有SwiftUI的UICollectionView +可能进行拖放重新排序? [英] UICollectionView with SwiftUI + Drag and drop reordering possible?

查看:118
本文介绍了带有SwiftUI的UICollectionView +可能进行拖放重新排序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在SwiftUI View(也使用SwiftUI View单元)中实现UICollectionView.所以我有一个Hosting + Representable Combo.

I'm implementing a UICollectionView inside my SwiftUI View (also with SwiftUI View cells). So I have a Hosting+Representable Combo.

现在,我想通过拖放来重新排列单元格,但是什么也没有发生.

Now I want to reorder my cells through drag and drop, but nothing happens.

想法是将longpressGesture与给定的函数canMoveItemAt和moveItemAt一起使用.

The idea is to use a longpressGesture together with the given functions canMoveItemAt and moveItemAt.

这是完整的代码:

import SwiftUI
import UIKit

struct ContentView: View {
    var body: some View {
        CollectionComponent()
    }
}

struct CollectionComponent : UIViewRepresentable {
    func makeCoordinator() -> CollectionComponent.Coordinator {
        Coordinator(data: [])
    }

    class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
        var data: [String] = []

        init(data: [String]) {

            for index in (0...20) {
                self.data.append("\(index)")
            }

        }

        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            data.count
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
            cell.cellView.rootView = AnyView(CellView(text: data[indexPath.item]))
            return cell
        }

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

        func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
            print("Changing the cell order, moving: \(sourceIndexPath.row) to \(destinationIndexPath.row)")
        }
    }

    func makeUIView(context: Context) -> UICollectionView {

        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.itemSize = CGSize(width: 150, height: 150)
        layout.sectionInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)

        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.backgroundColor = .white
        collectionView.dataSource = context.coordinator
        collectionView.delegate = context.coordinator
        collectionView.register(Cell.self, forCellWithReuseIdentifier: "cell")

        let longPressGesture = UILongPressGestureRecognizer(target: self, action: Selector(("handleLongGesture:")))
        collectionView.addGestureRecognizer(longPressGesture)

        func handleLongGesture(gesture: UILongPressGestureRecognizer) {

            switch(gesture.state) {

            case UIGestureRecognizerState.began:
                guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
                    break
                }
                collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
            case UIGestureRecognizerState.changed:
                collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
            case UIGestureRecognizerState.ended:
                collectionView.endInteractiveMovement()
            default:
                collectionView.cancelInteractiveMovement()
            }
        }

        return collectionView
    }

    func updateUIView(_ uiView: UICollectionView, context: Context) {

    }
}


class Cell: UICollectionViewCell {
    public var cellView = UIHostingController(rootView: AnyView(EmptyView()))

    public override init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    private func configure() {
        contentView.addSubview(cellView.view)

        cellView.view.preservesSuperviewLayoutMargins = false
        cellView.view.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            cellView.view.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
            cellView.view.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
            cellView.view.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
            cellView.view.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor),
        ])
    }
}

struct CellView: View {
    let text: String

    var body: some View {
        ZStack {
            Text(text)
        }
        .frame(width: 150, height: 150)
        .background(Color.blue)
    }
}

谢谢!

推荐答案

只需使用 UICollectionViewDragDelegate UICollectionViewDropDelegate 将单元格视图拖放到 UICollectionView .它完美地工作.这是示例代码...

Just use UICollectionViewDragDelegate and UICollectionViewDropDelegate to drag and drop cell views inside UICollectionView. It works perfectly. Here is the sample code...

struct ContentView: View {
     var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    var body: some View {
        GeometryReader { proxy in
            GridView(self.numbers, proxy: proxy) { number in
                Image("image\(number)")
                .resizable()
                .scaledToFill()
            }
        }
    }
}
struct GridView<CellView: View>: UIViewRepresentable {
    let cellView: (Int) -> CellView
    let proxy: GeometryProxy
    var numbers: [Int]
    init(_ numbers: [Int], proxy: GeometryProxy, @ViewBuilder cellView: @escaping (Int) -> CellView) {
        self.proxy = proxy
        self.cellView = cellView
        self.numbers = numbers
    }
    func makeUIView(context: Context) -> UICollectionView {
        let layout = UICollectionViewFlowLayout()
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0

        let collectionView = UICollectionView(frame: UIScreen.main.bounds, collectionViewLayout: layout)
        collectionView.backgroundColor = .white
        collectionView.register(GridCellView.self, forCellWithReuseIdentifier: "CELL")

        collectionView.dragDelegate = context.coordinator //to drag cell view
        collectionView.dropDelegate = context.coordinator //to drop cell view

        collectionView.dragInteractionEnabled = true
        collectionView.dataSource = context.coordinator
        collectionView.delegate = context.coordinator
        collectionView.contentInset = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
        return collectionView
    }
    func updateUIView(_ uiView: UICollectionView, context: Context) { }
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    class Coordinator: NSObject, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDragDelegate, UICollectionViewDropDelegate {
        var parent: GridView
        init(_ parent: GridView) {
            self.parent = parent
        }
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return parent.numbers.count
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CELL", for: indexPath) as! GridCellView
            cell.backgroundColor = .clear
            cell.cellView.rootView = AnyView(parent.cellView(parent.numbers[indexPath.row]).fixedSize())
            return cell
        }

        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: ((parent.proxy.frame(in: .global).width - 8) / 3), height: ((parent.proxy.frame(in: .global).width - 8) / 3))
        }

        //Provides the initial set of items (if any) to drag.
        func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
            let item = self.parent.numbers[indexPath.row]
            let itemProvider = NSItemProvider(object: String(item) as NSString)
            let dragItem = UIDragItem(itemProvider: itemProvider)
            dragItem.localObject = item
            return [dragItem]
        }

        //Tells your delegate that the position of the dragged data over the collection view changed.
        func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
            if collectionView.hasActiveDrag {
                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
            }
            return UICollectionViewDropProposal(operation: .forbidden)
        }

        //Tells your delegate to incorporate the drop data into the collection view.
        func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
            var destinationIndexPath: IndexPath
            if let indexPath = coordinator.destinationIndexPath {
                destinationIndexPath = indexPath
            } else {
                let row = collectionView.numberOfItems(inSection: 0)
                destinationIndexPath = IndexPath(item: row - 1, section: 0)
            }
            if coordinator.proposal.operation == .move {
                self.reorderItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
            }
        }
        private func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
            if let item = coordinator.items.first, let sourceIndexPath = item.sourceIndexPath {
                collectionView.performBatchUpdates({
                    self.parent.numbers.remove(at: sourceIndexPath.item)
                    self.parent.numbers.insert(item.dragItem.localObject as! Int, at: destinationIndexPath.item)
                    collectionView.deleteItems(at: [sourceIndexPath])
                    collectionView.insertItems(at: [destinationIndexPath])
                }, completion: nil)
                coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
            }
        }
    }
}
class GridCellView: UICollectionViewCell {
    public var cellView = UIHostingController(rootView: AnyView(EmptyView()))
    public override init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }
    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }
    private func configure() {
        contentView.addSubview(cellView.view)
        cellView.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            cellView.view.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 5),
            cellView.view.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -5),
            cellView.view.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
            cellView.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),
        ])
        cellView.view.layer.masksToBounds = true
    }
}

您可以在此处 https://media.giphy.com/查看最终结果媒体/UPuWLauQepwi5Q77PA/giphy.gif 谢谢.X_X

You can see the final result here https://media.giphy.com/media/UPuWLauQepwi5Q77PA/giphy.gif Thanks. X_X

这篇关于带有SwiftUI的UICollectionView +可能进行拖放重新排序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆