2010-10-01 10 views
8

Tôi có một đối tượng UIView X được chứa trong một đối tượng UIView A. Tôi muốn có thể chạm vào X và loại bỏ nó khỏi đối tượng A và di chuyển nó vào đối tượng B (UIView khác). Cả hai đối tượng A & B đều nằm trong cùng một siêu UIView.Kéo UIView giữa UIViews

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

Đây là những gì tôi có cho đến nay.

@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

Bạn đã bao giờ hoàn thành này? Tôi đang cố gắng để làm chính xác như vậy nhưng khi tôi thêm X cho cha mẹ trên cùng như bạn làm trong touchesBegan nó biến mất. Vì vậy, tôi không thể xem được khi tôi kéo nó xung quanh. giúp đỡ của bạn được đánh giá cao. –

Trả lời

10

Gọi [[touches anyObject] locationInView: self.superview] để nhận điểm dưới ngón tay trong chế độ xem vùng chứa. Sau đó, hãy gửi self.superview -hitTest:withEvent: để tìm hiểu chế độ xem X ở bên trong. Lưu ý rằng nó sẽ luôn trả về X, vì vậy bạn sẽ phải ghi đè hoặc -pointIsInside:withEvent: hoặc -hitTest:withEvent: để trả về số không khi bạn đang kéo. Loại bùn này là lý do tôi sẽ thực hiện theo dõi như vậy trong chế độ xem vùng chứa chứ không phải trong chế độ xem được kéo.

+0

Bạn sẽ triển khai theo dõi trong chế độ xem vùng chứa như thế nào? –

+1

Trên suy nghĩ thứ hai, có những lý do hoàn toàn hợp lệ để thực hiện tất cả theo dõi trong X, vì vậy không bao giờ xảy ra. Nhân tiện, trong khi theo dõi bạn có thể kiểm tra các khung của X và B cho giao lộ thay vì kiểm tra vị trí của ngón tay. Tùy thuộc vào nhu cầu của bạn, nó có thể tốt hơn cho phản hồi trực quan. – Costique

0

Với iOS 11, bạn có thể giải quyết sự cố của mình bằng API kéo và thả. Đoạn mã Swift 4 sau đây cho thấy cách thực hiện.


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