2010-10-01 11 views
8

Mam obiekt UIView, który jest zawarty w obiekcie UIView A. Chcę móc dotknąć X i usunąć go z obiektu A i przenieść go do obiektu B (inny UIView). Obie Obiektu A & B są wewnątrz tego samego super UIView.Przeciągnij UIView między UIViews

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

To jest to, co do tej pory miałem.

@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

Czy kiedykolwiek zakończyć to? Próbuję zrobić dokładnie to samo, ale kiedy dodaję X do najwyższego rodzica, jak to robisz w dotknięciach Began, znika. Nie mogę zobaczyć widoku, gdy go przeciągam. Twoja pomoc jest bardzo cenna. –

Odpowiedz

10

Zadzwoń pod [[touches anyObject] locationInView: self.superview], aby uzyskać punkt pod palcem w widoku kontenera. Następnie wyślij self.superview -hitTest:withEvent:, aby dowiedzieć się, jaki widok X znajduje się w środku. Zwróć uwagę, że zawsze zwróci X, więc będziesz musiał zastąpić albo -pointIsInside:withEvent: lub -hitTest:withEvent:, aby zwrócić zero podczas przeciągania. Ten rodzaj kludu jest powodem, dla którego zaimplementowałem takie śledzenie w widoku kontenera, a nie w widoku przeciągniętym.

+0

Jak zaimplementować śledzenie w widoku kontenera? –

+1

Po krótkiej myśli, istnieją doskonale uzasadnione powody, aby robić wszystkie śledzenie w X, więc nieważne. Nawiasem mówiąc, podczas śledzenia możesz testować ramki X i B dla skrzyżowania, zamiast sprawdzać, gdzie znajduje się palec. W zależności od Twoich potrzeb może być jeszcze lepiej dla wizualnej informacji zwrotnej. – Costique

0

Dzięki systemowi iOS 11 można rozwiązać problem za pomocą interfejsów API przeciągania i upuszczania. Poniższy kod Swift 4 pokazuje, jak to zrobić.


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