2010-10-01 5 views
8

У меня есть объект UIView X, который содержится в объекте UIView A. Я хочу иметь возможность коснуться X и удалить его из объекта A и перенести его в объект B (другой UIView). Оба объекта A & B находятся внутри того же супер UIView.Перетащить UIView между UIViews

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

Это то, что у меня есть до сих пор.

@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

ли вы когда-нибудь закончить это? Я пытаюсь сделать то же самое, но когда я добавляю X в самый верхний родитель, как вы это делаете в touchhesBegan, он исчезает. Поэтому я не могу видеть представление, когда я его перетаскиваю. Ваша помощь высоко ценится. –

ответ

10

Позвоните [[touches anyObject] locationInView: self.superview], чтобы получить точку под пальцем в виде контейнера. Затем отправьте self.superview -hitTest:withEvent:, чтобы узнать, что находится внутри. Обратите внимание, что он всегда будет возвращать X, поэтому вам придется переопределить либо -pointIsInside:withEvent:, либо -hitTest:withEvent:, чтобы вернуть нуль во время перетаскивания. Этот тип kludge является причиной того, что я буду реализовывать такое отслеживание в представлении контейнера, а не в перетаскиваемом виде.

+0

Как реализовать отслеживание в представлении контейнера? –

+1

С другой стороны, есть вполне обоснованные причины, чтобы делать все отслеживание в X, так что никогда не думайте. Кстати, при отслеживании вы можете проверять кадры X и B для пересечения, вместо того, чтобы проверять, где находится палец. В зависимости от ваших потребностей, это может быть даже лучше для визуальной обратной связи. – Costique

0

С iOS 11 вы можете решить вашу проблему с помощью API перетаскивания. Следующий код Swift 4 показывает, как это сделать.


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

Смежные вопросы