'Ability to shorten UIDragInteraction's long press timing
I am currently using UIDragInteraction and UIDropInteraction made available in iOS 11 to make a simple drag and drop feature, where user could drag an UIImageView onto a UIView.
I realized that one unintuitive element to this is that the UIDragInteraction requires a long press of at least a second to work. I was wondering if there is a way to shorten the long press duration? The docs on Apple doesn't seem to highlight this.
Thanks!
Implementation pasted below for reference:
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
}
}
}
}
Solution 1:[1]
There's no obvious way to do this, but I was just facing the same problem and took a peek into the gesture recognizers of the view that the dragInteraction is attached to. It a _UIDragLiftGestureRecognizer which is not part of the public API, but turns out this is just a subclass of UILongPressGestureRecognizer.
So, after having added your UIDragInteraction to your view, and after having added that view to the view hierachy (since I'm using a custom UIView subclass I just added it into didMoveToSuperview()), you can do something like this:
if let longPressRecognizer = gestureRecognizers?.compactMap({ $0 as? UILongPressGestureRecognizer}).first {
longPressRecognizer.minimumPressDuration = 0.1 // your custom value
}
Solution 2:[2]
I was trying to do this from Xamarin.iOS inside an UIView implementing the IUIDragInteractionDelegate interface. In its constructor I made a SetupDragNDrop method that allows to drag the view without that default delay/latency to catch the view. I leave the code down below in case it's useful for somebody else:
#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;
}
Solution 3:[3]
You can use this code to avoid affecting other gestures:
let gestureRecognizers = self.view.gestureRecognizers?.compactMap({ $0 as? UILongPressGestureRecognizer })
let liftGesture = gestureRecognizers?.filter({ String(describing: type(of: $0)) == "_UIDragLiftGestureRecognizer" }).first
liftGesture?.minimumPressDuration = minimumPressDuration//use custom value
As well this can help someone customising 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({ $0 as? UILongPressGestureRecognizer })
let liftGesture = gestureRecognizers?.filter({ String(describing: type(of: $0)) == "_UIDragLiftGestureRecognizer" }).first
liftGesture?.minimumPressDuration = minimumPressDuration
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | julsh |
| Solution 2 | Juansero29 |
| Solution 3 | ??????? ?????? |
