次の方法で共有


Xamarin.iOS でのドラッグ アンド ドロップ

iOS 11 のドラッグ アンド ドロップの実装

iOS 11 には、iPad 上のアプリケーション間でデータをコピーするためのドラッグ アンド ドロップのサポートが組み込まれています。 ユーザーは、並べて配置されたアプリからすべての種類のコンテンツを選択してドラッグすることができます。または、アプリのオープンやデータのドロップの許可をトリガーするアプリ アイコンの上にドラッグすることができます。

カスタム アプリから Notes アプリへのドラッグ アンド ドロップの例

Note

iOS 15 より前は、ドラッグ アンド ドロップは iPhone の同じアプリ内でのみ使用できます。 iOS 15 では、アプリ間のドラッグ アンド ドロップが導入されています。

コンテンツを作成または編集できる任意の場所で、ドラッグ アンド ドロップ操作をサポートすることを検討してください。

  • テキスト コントロールでは、iOS 11 に対して作成されたすべてのアプリで、追加の作業なくドラッグ アンド ドロップがサポートされます。
  • テーブル ビューとコレクション ビューには、ドラッグ アンド ドロップ動作を簡単に追加する iOS 11 の機能強化が含まれています。
  • その他のビューでは、追加のカスタマイズを行えば、ドラッグ アンド ドロップをサポートできます。

ドラッグ アンド ドロップのサポートをアプリに追加する場合は、さまざまなレベルのコンテンツの忠実性を提供できます。たとえば、書式設定されたテキストとプレーン テキストの両方のバージョンのデータを指定して、受信アプリがドラッグ先に最適なデータを選択できるようにすることができます。 また、ドラッグの視覚化をカスタマイズしたり、一度に複数の項目をドラッグできるようにしたりすることもできます。

テキスト コントロールを使用したドラッグ アンド ドロップ

UITextViewUITextField は、選択したテキストのドラッグと、テキスト コンテンツのドロップを自動的にサポートします。

UITableView を使用したドラッグ アンド ドロップ

UITableView には、テーブル行とのドラッグ アンド ドロップ操作の処理が組み込まれており、既定の動作を有効にするために必要なメソッドはごくわずかです。

次の 2 つのインターフェイスが関係します。

  • IUITableViewDragDelegate – テーブル ビューでドラッグが開始されたときに情報をパッケージ化します。
  • IUITableViewDropDelegate – ドロップが試行され完了したときに情報を処理します。

サンプルでは、これら 2 つのインターフェイスはどちらも、デリゲートとデータ ソースと共に UITableViewController クラスに実装されています。 これらは ViewDidLoad メソッドで割り当てられます。

this.TableView.DragDelegate = this;
this.TableView.DropDelegate = this;

これら 2 つのインターフェイスに必要な最小限のコードを以下で説明します。

テーブル ビューのドラッグ デリゲート

テーブル ビューからの行のドラッグをサポートするために "必要な" メソッドは GetItemsForBeginningDragSession のみです。 ユーザーが行のドラッグを開始すると、このメソッドが呼び出されます。

実装を次に示します。 ドラッグされた行に関連付けられているデータを取得し、それをエンコードし、NSItemProvider を構成します。これにより、アプリケーションが操作の "ドロップ" 部分を処理する方法 (たとえば、例のデータ型 PlainText を処理できるかどうか) が決まります。

public UIDragItem[] GetItemsForBeginningDragSession (UITableView tableView,
  IUIDragSession session, NSIndexPath indexPath)
{
  // gets the 'information' to be dragged
  var placeName = model.PlaceNames[indexPath.Row];
  // convert to NSData representation
  var data = NSData.FromString(placeName, NSStringEncoding.UTF8);
  // create an NSItemProvider to describe the data
  var itemProvider = new NSItemProvider();
  itemProvider.RegisterDataRepresentation(UTType.PlainText,
                                NSItemProviderRepresentationVisibility.All,
                                (completion) =>
  {
    completion(data, null);
    return null;
  });
  // wrap in a UIDragItem
  return new UIDragItem[] { new UIDragItem(itemProvider) };
}

ドラッグ デリゲートには、ドラッグ動作をカスタマイズするために実装できる省略可能なメソッドが多数あります。たとえば、ターゲット アプリで利用できる複数のデータ表現 (書式設定テキストやプレーン テキスト、図面のベクターやビットマップ バージョンなど) を提供します。 また、同じアプリ内でドラッグ アンド ドロップするときに使用するカスタム データ表現も提供できます。

テーブル ビューのドロップ デリゲート

ドロップ デリゲートのメソッドは、テーブル ビュー上でドラッグ操作が行われるか、その上で完了したときに呼び出されます。 必須のメソッドにより、データのドロップを許可するかどうか、およびドロップが完了した場合に実行されるアクションが決まります。

  • CanHandleDropSession – ドラッグが進行中で、アプリケーションでドロップされる可能性がある間に、このメソッドは、ドラッグされているデータのドロップを許可するかどうかを決定します。
  • DropSessionDidUpdate – ドラッグが進行中に、このメソッドを呼び出して、目的のアクションを決定します。 上でドラッグされているテーブル ビューからの情報、ドラッグ セッション、および可能なインデックス パスはすべて、ユーザーに提供される動作と視覚的フィードバックを決定するために使用できます。
  • PerformDrop – ユーザーがドロップを完了すると (指を離すことによって)、このメソッドはドラッグされているデータを抽出し、テーブル ビューを変更して 1 つまたは複数の新しい行にデータを追加します。

CanHandleDropSession

CanHandleDropSession は、ドラッグされているデータをテーブル ビューが受け入れられるかどうかを示します。 このコード スニペットでは、このテーブル ビューが文字列データを受け入れることを確認するために CanLoadObjects が使用されます。

public bool CanHandleDropSession(UITableView tableView, IUIDropSession session)
{
  return session.CanLoadObjects(typeof(NSString));
}

DropSessionDidUpdate

DropSessionDidUpdate メソッドは、ドラッグ操作が進行中に繰り返し呼び出され、ユーザーに視覚的な手掛かりを提供します。

次のコードでは、操作が現在のテーブル ビューで開始したかどうかを決定するために HasActiveDrag が使用されます。 その場合、移動できるのは 1 行だけです。 ドラッグが別のソースからの場合は、コピー操作が示されます。

public UITableViewDropProposal DropSessionDidUpdate(UITableView tableView, IUIDropSession session, NSIndexPath destinationIndexPath)
{
  // The UIDropOperation.Move operation is available only for dragging within a single app.
  if (tableView.HasActiveDrag)
  {
    if (session.Items.Length > 1)
    {
        return new UITableViewDropProposal(UIDropOperation.Cancel);
    } else {
        return new UITableViewDropProposal(UIDropOperation.Move, UITableViewDropIntent.InsertAtDestinationIndexPath);
    }
  } else {
    return new UITableViewDropProposal(UIDropOperation.Copy, UITableViewDropIntent.InsertAtDestinationIndexPath);
  }
}

ドロップ操作は、CancelMove、または Copy のいずれかにすることができます。

ドロップの目的は、新しい行を挿入するか、既存の行にデータを追加することです。

PerformDrop

PerformDrop メソッドは、ユーザーが操作を完了したときに呼び出され、ドロップしたデータを反映するようにテーブル ビューとデータ ソースを変更します。

public void PerformDrop(UITableView tableView, IUITableViewDropCoordinator coordinator)
{
  NSIndexPath indexPath, destinationIndexPath;
  if (coordinator.DestinationIndexPath != null)
  {
    indexPath = coordinator.DestinationIndexPath;
    destinationIndexPath = indexPath;
  }
  else
  {
    // Get last index path of table view
    var section = tableView.NumberOfSections() - 1;
    var row = tableView.NumberOfRowsInSection(section);
    destinationIndexPath = NSIndexPath.FromRowSection(row, section);
  }
  coordinator.Session.LoadObjects(typeof(NSString), (items) =>
  {
    // Consume drag items
    List<string> stringItems = new List<string>();
    foreach (var i in items)
    {
      var q = NSString.FromHandle(i.Handle);
      stringItems.Add(q.ToString());
    }
    var indexPaths = new List<NSIndexPath>();
    for (var j = 0; j < stringItems.Count; j++)
    {
      var indexPath1 = NSIndexPath.FromRowSection(destinationIndexPath.Row + j, destinationIndexPath.Section);
      model.AddItem(stringItems[j], indexPath1.Row);
      indexPaths.Add(indexPath1);
    }
    tableView.InsertRows(indexPaths.ToArray(), UITableViewRowAnimation.Automatic);
  });
}

大規模なデータ オブジェクトを非同期的に読み込むには、追加のコードを追加できます。