2010-10-01 19 views
8

Tengo un objeto UIView X que está contenido en un objeto UIView A. Quiero poder tocar X y eliminarlo del objeto A y moverlo al objeto B (otra UIView). Ambos objetos A & B están dentro de la misma super UIView.Arrastrar UIView entre UIViews

A  B 
_____ _____ 
| | | | 
| X | -> | | 
|___| |___| 

Esto es lo que tengo hasta ahora.

@implementation X_UIView 

float deltaX; 
float deltaY; 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    [self.superview.superview addSubview:self]; //pop dragged view outside of container view 

    CGPoint beginCenter = self.center; 

    UITouch * touch = [touches anyObject]; 
    CGPoint touchPoint = [touch locationInView:self.superview]; 

    deltaX = touchPoint.x - beginCenter.x; 
    deltaY = touchPoint.y - beginCenter.y; 
} 

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { 
    UITouch * touch = [touches anyObject]; 
    CGPoint touchPoint = [touch locationInView:self.superview]; 

    // Set the correct center when touched 
    touchPoint.x -= deltaX; 
    touchPoint.y -= deltaY; 

    self.center = touchPoint; 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    //discover view that event ended was over and add self as a subview. 
} 

@end 
+0

¿Alguna vez completado esto? Intento hacer exactamente lo mismo, pero cuando agrego X al padre superior como lo haces en touchesBegan, desaparece. Así que no puedo ver la vista mientras la arrastro. Tu ayuda es altamente apreciada. –

Respuesta

10

Llame al [[touches anyObject] locationInView: self.superview] para obtener el punto debajo del dedo en la vista del contenedor. A continuación, envíe self.superview -hitTest:withEvent: para conocer la vista que X tiene dentro. Tenga en cuenta que siempre devolverá X, por lo que tendrá que anular -pointIsInside:withEvent: o -hitTest:withEvent: para devolver el nulo mientras está arrastrando. Este tipo de kludge es la razón por la que implementaría dicho seguimiento en la vista de contenedor, en lugar de hacerlo en una vista arrastrada.

+0

¿Cómo implementaría el seguimiento en la vista de contenedor? –

+1

Pensándolo bien, hay razones perfectamente válidas para hacer todo el seguimiento en X, así que no importa. Por cierto, durante el seguimiento puede probar los fotogramas de X y B para la intersección en lugar de verificar dónde está el dedo. Dependiendo de tus necesidades, podría ser incluso mejor para la retroalimentación visual. – Costique

0

Con iOS 11, puede resolver su problema con las API de arrastrar y soltar. El siguiente código de Swift 4 muestra cómo hacerlo.


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 = NSKeyedUnarchiver.unarchiveObject(with: 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 { 
      let data = NSKeyedArchiver.archivedData(withRootObject: view) 
      completionHandler(data, nil) 
     } 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 
     redView.addInteraction(greenViewDragInteraction) 
     greenView.addInteraction(greenViewDropInteraction) 

     let redViewDropInteraction = UIDropInteraction(delegate: self) 
     let redViewDragInteraction = UIDragInteraction(delegate: self) 
     redViewDragInteraction.isEnabled = true 
     greenView.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) 
     } 
    } 

} 

enter image description here

Cuestiones relacionadas