April 13th, 2018

Using the iOS 11 Drag and Drop APIs in Xamarin.iOS Apps

Brad Umbaugh
Senior Content Developer

iOS 11 introduced drag and drop, a feature that allows users to easily move or copy data within or between apps. With drag and drop, users can:

  • Select items to drag
  • Add extra items to a drag after it has started
  • Transfer data asynchronously between applications

On iPads, drag and drop works within or between applications. On iPhones, drag and drop is restricted to a single app.

Drag and Drop Sample App

This post describes how the drag and drop APIs are used in a single-screen iPhone app that has users sort even and odd numbers:

The EvenOddNumberDrag sample

To follow along, download the code for the EvenOddNumberDrag app.

Enable User Interaction

Three elements of the app’s user interface (found in Main.storyboard) are relevant to dragging and dropping:

  1. NumberLabel displays the number which is to be dragged.
  2. EvenNumbersLabel is the label on which even numbers should be dropped.
  3. OddNumbersLabel is the label on which odd numbers should be dropped.

Since users will be interacting with these labels, each must have its UserInteractionEnabled property set to true. This is done in ViewController.cs:


protected void SetupDragAndDrop()
{
   NumberLabel.UserInteractionEnabled = true;
   EvenNumbersLabel.UserInteractionEnabled = true;
   OddNumbersLabel.UserInteractionEnabled = true;
   // …
}

Enable Dragging

A basic drag interaction requires only a few pieces:

  • A UIDragInteraction object that is attached to the view that will be draggable and explicitly enabled:
    
    protected void SetupDragAndDrop()
    {
        // …
        var numberDragInteraction = new UIDragInteraction(this);
        NumberLabel.AddInteraction(numberDragInteraction);		
    
        // On iPad, this defaults to true. On iPhone, this defaults to 
        // false. Since this app should work on the iPhone, enable the 
        // drag interaction.
        numberDragInteraction.Enabled = true;
        // …
    }
    
  • An IUIDragInteractionDelegate that acts as the delegate for the drag interaction object. In this sample, ViewController itself is the IUIDragInteractionDelegate. Only one of the delegate methods needs to be implemented:
     
    public UIDragItem[] GetItemsForBeginningSession(UIDragInteraction interaction, IUIDragSession session) 
    { 
        bool isEven = Convert.ToInt16(NumberLabel.Text) % 2 == 0; 
        var provider = new NSItemProvider(new NSString(NumberLabel.Text)); 
        var item = new UIDragItem(provider) 
        { 
            LocalObject = new NSNumber(isEven) 
        }; 
        return new UIDragItem[] { item } ; 
    } 
    

    This method returns an array of UIDragItem objects, each representing a piece of data to be included in the drag and drop operation. Each drag item contains an NSItemProvider that holds a representation of the data to be transferred.

    In this case, the NSItemProvider is given an NSString, which implements the INSItemProviderWriting interface, one of the data representations supported by NSItemProvider.

    The UIDragItem has a LocalObject property that is accessible only within the application that initiated the drag. Since this example focuses on a single app, for convenience GetItemsForBeginningSession sets the LocalObject to a NSNumber. This represents whether or not the dragged number is even.

Enable Dropping

To enable a drop interaction, you will need:

  • A UIDropInteraction object for each view on which items can be dropped:
    
    protected void SetupDragAndDrop()
    {
        // …
        evenDropInteraction = new UIDropInteraction(this);
        EvenNumbersLabel.AddInteraction(evenDropInteraction);
        oddDropInteraction = new UIDropInteraction(this);
        OddNumbersLabel.AddInteraction(oddDropInteraction);
    }
    
  • An IUIDropInteractionDelegate object. In this sample, ViewController serves as the drop delegate.
  • An implementation of the SessionDidUpdate method from the IUIDropInteractionDelegate interface extension methods:
        
        [Export("dropInteraction:sessionDidUpdate:")] 
        public UIDropProposal SessionDidUpdate(UIDropInteraction interaction, IUIDropSession session) 
        { 
            UIDropProposal proposal; 
            var isEven = (session.Items[0].LocalObject as NSNumber).BoolValue; 
            if (interaction == oddDropInteraction && !isEven) 
            { 
                proposal = new UIDropProposal(UIDropOperation.Copy); 
            } 
            else if (interaction == evenDropInteraction && isEven) 
            { 
                proposal = new UIDropProposal(UIDropOperation.Copy); 
            } 
            else 
            { 
                proposal = new UIDropProposal(UIDropOperation.Forbidden);
            }
            return proposal;
        }
        

    This method is called any time there’s a change that will potentially affect the outcome of the drop; for example, when the drop location changes in the target view because the user has moved the item being dragged. It looks at the IUIDropSession to determine whether or not the drop should proceed, proposing (with a UIDropProposal containing a UIDropOperation) to move the dropped data, copy the dropped data, cancel the drop, or forbid the drop.

    In this example, LocalObject (set up in GetItemsForBeginningSession) provides an easy way to determine whether or not the dragged number is even or odd. A copy is proposed if the potential drop is valid, and the drop is forbidden if it is invalid.

    Proposing a copy or forbidding a drop both add helpful icons to the dragged item:

    Drop indicators

  • An implementation of the PerformDrop method from the IUIDropInteractionDelegate interface extension methods:
    
    [Export("dropInteraction:performDrop:")]
    public void PerformDrop(UIDropInteraction interaction, IUIDropSession session)
    {
        var label = interaction == oddDropInteraction ? OddNumbersLabel : EvenNumbersLabel;
        session.LoadObjects(strings =>
        {
            if (String.IsNullOrEmpty(label.Text))
            {
                label.Text = strings[0];
            }
            else
            {
                label.Text = $"{strings[0]}, {label.Text}";
            }
        });
        GenerateNumber();
    }
    

    This method is used for dropping numbers on both the even bucket and the odd bucket. The drop target is determined by checking the identity of the passed-in drop interaction.

  • Lastly, the LoadObjects method on IUIDropSession fetches the data and adds it to the even or odd bucket.

Learn More

This post only scratches the surface of the iOS 11 drag and drop APIs. To dig deeper, check out the following resources:

Have questions or comments about this post? Discuss them in the Xamarin forums!

Category
Developers
Topics
iOS

Author

Brad Umbaugh
Senior Content Developer

Brad Umbaugh is a Senior Content Developer focused on documentation for Xamarin’s iOS, tvOS, watchOS, and macOS support. At Microsoft, he was a program manager on the Bing APIs team and a TSP. He lives in Denver, CO.

0 comments

Discussion are closed.