Drag UIView between UIViews

后端 未结 2 1597
执笔经年
执笔经年 2020-12-09 06:31

I have a UIView object X that is contained in an UIView object A. I want to be able to touch X and remove it from object A and move it into object B (another UIView). Both O

相关标签:
2条回答
  • 2020-12-09 06:52

    Call [[touches anyObject] locationInView: self.superview] to get the point under the finger in the container view. Then send self.superview -hitTest:withEvent: to find out the view X is inside. Note that it will always return X, so you will have to override either -pointIsInside:withEvent: or -hitTest:withEvent: to return nil while you're dragging. This kind of kludge is the reason I would implement such tracking in the container view, rather than in a dragged view.

    0 讨论(0)
  • 2020-12-09 07:06

    With Swift 5 and iOS 12, you can solve your problem with Drag and Drop APIs. The following sample code shows how to do.


    ViewContainer.swift

    import MobileCoreServices
    import UIKit
    
    enum ViewContainerError: Error {
        case invalidType, unarchiveFailure
    }
    
    class ViewContainer: NSObject {
    
        let view: UIView
    
        required init(view: UIView) {
            self.view = view
        }
    
    }
    
    extension ViewContainer: NSItemProviderReading {
    
        static var readableTypeIdentifiersForItemProvider = [kUTTypeData as String]
    
        static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> Self {
            if typeIdentifier == kUTTypeData as String {
                guard let view = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UIView else { throw ViewContainerError.unarchiveFailure }
                return self.init(view: view)
            } else {
                throw ViewContainerError.invalidType
            }
        }
    
    }
    
    extension ViewContainer: NSItemProviderWriting {
    
        static var writableTypeIdentifiersForItemProvider = [kUTTypeData as String]
    
        func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress? {
            if typeIdentifier == kUTTypeData as String {
                do {
                    let data = try NSKeyedArchiver.archivedData(withRootObject: view, requiringSecureCoding: false)
                    completionHandler(data, nil)
                } catch {
                    completionHandler(nil, error)
                }
            } else {
                completionHandler(nil, ViewContainerError.invalidType)
            }
            return nil
        }
    
    }
    

    ViewController.swift

    import UIKit
    
    class ViewController: UIViewController {
    
        let redView = UIView()
        let greenView = UIView()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let blueView = UIView()
            blueView.backgroundColor = .blue
    
            greenView.backgroundColor = .green
            greenView.isUserInteractionEnabled = true
            greenView.addSubview(blueView)
            setConstraintsInSuperView(forView: blueView)
    
            redView.backgroundColor = .red
            redView.isUserInteractionEnabled = true
    
            let greenViewDropInteraction = UIDropInteraction(delegate: self)
            let greenViewDragInteraction = UIDragInteraction(delegate: self)
            greenViewDragInteraction.isEnabled = true
            greenView.addInteraction(greenViewDragInteraction)
            greenView.addInteraction(greenViewDropInteraction)
    
            let redViewDropInteraction = UIDropInteraction(delegate: self)
            let redViewDragInteraction = UIDragInteraction(delegate: self)
            redViewDragInteraction.isEnabled = true
            redView.addInteraction(redViewDragInteraction)
            redView.addInteraction(redViewDropInteraction)
    
            let stackView = UIStackView(arrangedSubviews: [greenView, redView])
            view.addSubview(stackView)
            stackView.distribution = .fillEqually
            stackView.frame = view.bounds
            stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        }
    
    }
    
    extension ViewController {
    
        // MARK: Helper methods
    
        func setConstraintsInSuperView(forView subView: UIView) {
            subView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[subView]-|", options: [], metrics: nil, views: ["subView": subView]))
            NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[subView]-|", options: [], metrics: nil, views: ["subView": subView]))
        }
    
    }
    
    extension ViewController: UIDragInteractionDelegate {
    
        func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
            guard let containedView = interaction.view?.subviews.first else { return [] }
            let viewContainer = ViewContainer(view: containedView)
            let itemProvider = NSItemProvider(object: viewContainer)
            let item = UIDragItem(itemProvider: itemProvider)
            item.localObject = viewContainer.view
            return [item]
        }
    
        func dragInteraction(_ interaction: UIDragInteraction, sessionWillBegin session: UIDragSession) {
            guard let containedView = interaction.view?.subviews.first else { return }
            containedView.removeFromSuperview()
        }
    
        func dragInteraction(_ interaction: UIDragInteraction, previewForLifting item: UIDragItem, session: UIDragSession) -> UITargetedDragPreview? {
            guard let containedView = interaction.view?.subviews.first else { return nil }
            return UITargetedDragPreview(view: containedView)
        }
    
        func dragInteraction(_ interaction: UIDragInteraction, item: UIDragItem, willAnimateCancelWith animator: UIDragAnimating) {
            animator.addCompletion { _ in
                guard let containedView = item.localObject as? UIView else { return }
                interaction.view!.addSubview(containedView)
                self.setConstraintsInSuperView(forView: containedView)
            }
        }
    
        func dragInteraction(_ interaction: UIDragInteraction, prefersFullSizePreviewsFor session: UIDragSession) -> Bool {
            return true
        }
    
    }
    
    extension ViewController: UIDropInteractionDelegate {
    
        func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
            return session.canLoadObjects(ofClass: ViewContainer.self) && session.items.count == 1
        }
    
        func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
            let dropLocation = session.location(in: view)
            let operation: UIDropOperation
            if interaction.view!.frame.contains(dropLocation) && session.localDragSession != nil {
                operation = .move
            } else {
                operation = .cancel
            }
            return UIDropProposal(operation: operation)
        }
    
        func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
            session.loadObjects(ofClass: ViewContainer.self) { viewContainers in
                guard let viewContainers = viewContainers as? [ViewContainer], let viewContainer = viewContainers.first else { return }
                interaction.view!.addSubview(viewContainer.view)
                self.setConstraintsInSuperView(forView: viewContainer.view)
            }
        }
    
    }
    

    0 讨论(0)
提交回复
热议问题