能够缩短 UIDragInteraction 的长按时间

Ability to shorten UIDragInteraction's long press timing

我目前正在使用 UIDragInteractionUIDropInteraction 在 iOS 11 中可用来制作简单的拖放功能,用户可以将 UIImageView 拖到 UIView 上。

我意识到一个不直观的因素是 UIDragInteraction 需要长按至少一秒钟才能工作。我想知道是否有办法缩短长按时间? docs on Apple 似乎没有强调这一点。

谢谢!

下面粘贴的实现以供参考:

class ViewController: UIViewController {

    @IBOutlet var imageView: UIImageView!
    @IBOutlet var dropArea: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let dragInteraction = UIDragInteraction(delegate: self)
        imageView.addInteraction(dragInteraction)
        dragInteraction.isEnabled = true
        let dropInteraction = UIDropInteraction(delegate: self)
        dropArea.addInteraction(dropInteraction)
    }
}

extension ViewController: UIDragInteractionDelegate {
    func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
        guard let image = imageView.image
            else { return [] }

        let itemProvider = NSItemProvider(object: image)
        return [UIDragItem(itemProvider: itemProvider)]
    }
}

extension ViewController: UIDropInteractionDelegate {
    func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
        return UIDropProposal(operation: .copy)
    }

    func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
        guard let itemProvider = session.items.first?.itemProvider,
            itemProvider.canLoadObject(ofClass: UIImage.self)
            else { return }

        itemProvider.loadObject(ofClass: UIImage.self) { [weak self] loadedItem, error in
            guard let image = loadedItem as? UIImage
                else { return }

            DispatchQueue.main.async {
                self?.dropArea.image = image
            }
        }
    }
}

没有明显的方法可以做到这一点,但我只是遇到了同样的问题并查看了 dragInteraction 附加到的视图的手势识别器。它是 _UIDragLiftGestureRecognizer,它不是 public API 的一部分,但事实证明这只是 UILongPressGestureRecognizer.

的子类

因此,在将您的 UIDragInteraction 添加到您的视图之后,将该视图添加到视图层次结构之后(因为我使用的是自定义 UIView 子类,所以我只是将它添加到 didMoveToSuperview() ),你可以这样做:

if let longPressRecognizer = gestureRecognizers?.compactMap({ [=10=] as? UILongPressGestureRecognizer}).first {
    longPressRecognizer.minimumPressDuration = 0.1 // your custom value
}

我试图从 Xamarin.iOS 在实现 IUIDragInteractionDelegate 接口的 UIView 中执行此操作。在它的构造函数中,我创建了一个 SetupDragNDrop 方法,允许在没有默认 delay/latency 的情况下拖动视图来捕捉视图。我将代码留在下面,以防它对其他人有用:

    #region Private Fields
    private UIDragInteraction _UIDragInteraction;
    #endregion

    void Initialize()
    {
        SetupDragNDrop();
    }

    private void SetupDragNDrop()
    {
        UserInteractionEnabled = true;
        _UIDragInteraction = new UIDragInteraction(this);
        AddInteraction(_UIDragInteraction);

        // On iPad, this defaults to true. On iPhone, this defaults to 
        // false. Since this app should work on the iPhone, enable the the 
        // drag interaction.
        _UIDragInteraction.Enabled = true;

        SetupDragDelay();
    }

    private void SetupDragDelay()
    {

        UILongPressGestureRecognizer longPressGesture = new UILongPressGestureRecognizer();

        GestureRecognizers?.ToList().ForEach(gesture =>
        {
            var x = gesture as UILongPressGestureRecognizer;
            if (x != null)
            {
                longPressGesture = x;
            }
        });

        longPressGesture.MinimumPressDuration = 0.0;
    }

您可以使用这段代码来避免影响其他手势:

let gestureRecognizers = self.view.gestureRecognizers?.compactMap({ [=10=] as? UILongPressGestureRecognizer })
let liftGesture = gestureRecognizers?.filter({ String(describing: type(of: [=10=])) == "_UIDragLiftGestureRecognizer" }).first
liftGesture?.minimumPressDuration = minimumPressDuration//use custom value

此外,这可以帮助自定义 SwiftUI onDrag:

extension View {
    public func customizeOnDrag(minimumPressDuration: TimeInterval) -> some View {
        overlay(CustomizeOnDrag(minimumPressDuration: minimumPressDuration).frame(width: 0, height: 0))
    }
}

private struct CustomizeOnDrag: UIViewControllerRepresentable {
    private let minimumPressDuration: TimeInterval
    
    init(minimumPressDuration: TimeInterval) {
        self.minimumPressDuration = minimumPressDuration
    }
    
    func makeUIViewController(context: Context) -> UIViewControllerType {
        return UIViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        DispatchQueue.main.async {
            let gestureRecognizers = uiViewController.parent?.view.gestureRecognizers?.compactMap({ [=11=] as? UILongPressGestureRecognizer })
            let liftGesture = gestureRecognizers?.filter({ String(describing: type(of: [=11=])) == "_UIDragLiftGestureRecognizer" }).first
            liftGesture?.minimumPressDuration = minimumPressDuration
        }
    }
}