简体   繁体   English

UICollectionView 和 SwiftUI + 可以拖放重新排序吗?

[英]UICollectionView with SwiftUI + Drag and drop reordering possible?

I'm implementing a UICollectionView inside my SwiftUI View (also with SwiftUI View cells).我在我的 SwiftUI 视图(也与 SwiftUI 视图单元格)中实现了 UICollectionView。 So I have a Hosting+Representable Combo.所以我有一个 Hosting+Representable 组合。

Now I want to reorder my cells through drag and drop, but nothing happens.现在我想通过拖放重新排序我的单元格,但没有任何反应。

The idea is to use a longpressGesture together with the given functions canMoveItemAt and moveItemAt.这个想法是将longpressGesture与给定的函数canMoveItemAt和moveItemAt一起使用。

Here is the full code:这是完整的代码:

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)
    }
}

Thanks!谢谢!

Just use UICollectionViewDragDelegate and UICollectionViewDropDelegate to drag and drop cell views inside UICollectionView .只需使用UICollectionViewDragDelegateUICollectionViewDropDelegateUICollectionView中拖放单元格视图。 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
    }
}

You can see the final result here https://media.giphy.com/media/UPuWLauQepwi5Q77PA/giphy.gif Thanks.您可以在此处查看最终结果https://media.giphy.com/media/UPuWLauQepwi5Q77PA/giphy.gif谢谢。 X_X X_X

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM