Xamarin.iOS を使用したテーブルの編集
テーブル編集機能は、UITableViewSource
サブクラス内のメソッドをオーバーライドすることにより有効になります。 最も簡単な編集動作は、1 つのメソッド オーバーライドで実装できるスワイプして削除のジェスチャです。
より複雑な編集 (行の移動を含む) は、編集モードのテーブルで行うことができます。
スワイプして削除する
スワイプして削除の機能は、ユーザーが期待する iOS の自然なジェスチャです。
[削除] ボタンをセルに表示するスワイプ ジェスチャに影響を与える次の 3 つのメソッドオーバーライドがあります。
- CommitEditingStyle – テーブル ソースは、このメソッドがオーバーライドされているかどうかを検出し、スワイプして削除のジェスチャを自動的に有効にします。 このメソッドの実装により、
UITableView
でDeleteRows
が呼び出されてセルが非表示になります。また、基礎となるデータがモデル (たとえば、配列、辞書、データベースなど) から削除されます。 - CanEditRow – CommitEditingStyle がオーバーライドされた場合、すべての行が編集可能であると見なされます。 このメソッドが実装され、(いくつかの特定の行またはすべての行で) false が返される場合、そのセルではスワイプして削除のジェスチャは使用できません。
- TitleForDeleteConfirmation – 必要に応じて、[削除] ボタンのテキストを指定します。 このメソッドが実装されていない場合、ボタンのテキストは "削除" になります。
これらのメソッドは、次のように TableSource
クラスに実装されます。
public override void CommitEditingStyle (UITableView tableView, UITableViewCellEditingStyle editingStyle, Foundation.NSIndexPath indexPath)
{
switch (editingStyle) {
case UITableViewCellEditingStyle.Delete:
// remove the item from the underlying data source
tableItems.RemoveAt(indexPath.Row);
// delete the row from the table
tableView.DeleteRows (new NSIndexPath[] { indexPath }, UITableViewRowAnimation.Fade);
break;
case UITableViewCellEditingStyle.None:
Console.WriteLine ("CommitEditingStyle:None called");
break;
}
}
public override bool CanEditRow (UITableView tableView, NSIndexPath indexPath)
{
return true; // return false if you wish to disable editing for a specific indexPath or for all rows
}
public override string TitleForDeleteConfirmation (UITableView tableView, NSIndexPath indexPath)
{ // Optional - default text is 'Delete'
return "Trash (" + tableItems[indexPath.Row].SubHeading + ")";
}
この例では、UITableViewSource
がデータ ソースとして List<TableItem>
(文字列配列ではなく) を使用するように更新されています。これがコレクションからの項目の追加と削除をサポートしているためです。
編集モード
テーブルが編集モードになっている場合、各行に赤い "停止" ウィジェットが表示され、タッチすると [削除] ボタンが表示されます。 テーブルには、行をドラッグして順序を変更できることを示す "ハンドル" アイコンも表示されます。 次に示すように、TableEditMode サンプルはこれらの機能を実装しています。
UITableViewSource
には、テーブルの編集モードの動作に影響を与える次のような各種メソッドがあります。
- CanEditRow – 各行を編集できるかどうかを示します。 編集モードの間にスワイプして削除と削除の両方が行われないようにするには、false を返します。
- CanMoveRow – 移動 "ハンドル" を有効にするには true を返し、移動しないようにするには false を返します。
- EditingStyleForRow – テーブルが編集モードのときに、このメソッドの戻り値によって、セルに赤色の削除アイコンと緑色の追加アイコンのどちらが表示されるかが決まります。 行を編集不可にする必要がある場合は
UITableViewCellEditingStyle.None
を返します。 - MoveRow – テーブルに表示されるデータと一致するように基になるデータ構造を変更できるように、行が移動されると呼び出されます。
最初の 3 つのメソッドの実装は比較的簡単です。indexPath
を使用して特定の行の動作を変更する場合を除き、テーブル全体の戻り値をハードコードするだけです。
public override bool CanEditRow (UITableView tableView, NSIndexPath indexPath)
{
return true; // return false if you wish to disable editing for a specific indexPath or for all rows
}
public override bool CanMoveRow (UITableView tableView, NSIndexPath indexPath)
{
return true; // return false if you don't allow re-ordering
}
public override UITableViewCellEditingStyle EditingStyleForRow (UITableView tableView, NSIndexPath indexPath)
{
return UITableViewCellEditingStyle.Delete; // this example doesn't support Insert
}
MoveRow
の実装は少し複雑になります。これは、新しい順序に合わせて基になるデータ構造を変更する必要があるためです。 データは List
として実装されているため、以下のコードは、以前の場所にあるデータ項目を削除し、それを新しい場所に挿入します。 データが、たとえば "order" 列 を持つ SQLite データベース テーブルに保存されている場合、このメソッドは代わりに、いくつかの SQL 操作を実行してその列の数値を並べ替える必要があります。
public override void MoveRow (UITableView tableView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
{
var item = tableItems[sourceIndexPath.Row];
var deleteAt = sourceIndexPath.Row;
var insertAt = destinationIndexPath.Row;
// are we inserting
if (destinationIndexPath.Row < sourceIndexPath.Row) {
// add one to where we delete, because we're increasing the index by inserting
deleteAt += 1;
} else {
// add one to where we insert, because we haven't deleted the original yet
insertAt += 1;
}
tableItems.Insert (insertAt, item);
tableItems.RemoveAt (deleteAt);
}
最後に、テーブルを編集モードにするには、次のように、[編集] ボタンで SetEditing
を呼び出す必要があります
table.SetEditing (true, true);
また、ユーザーが編集を完了したときに、[完了] ボタンで編集モードをオフにする必要があります。
table.SetEditing (false, true);
行挿入の編集スタイル
テーブル内からの行の挿入は、一般的ではないユーザー インターフェイスです。標準の iOS アプリの主な例は、[連絡先の編集] スクリーンです。 このスクリーンショットは、行の挿入機能のしくみを示しています。編集モードでは、追加行が表示され、(クリックすると) 行が追加でデータに挿入されます。 編集が完了すると、一時的な [(新規追加)] 行は削除されます。
UITableViewSource
には、テーブルの編集モードの動作に影響を与える各種メソッドがあります。 これらのメソッドは、コード例で次のように実装されています。
- EditingStyleForRow – データを含む行に対して
UITableViewCellEditingStyle.Delete
を返し、最後の行に対してUITableViewCellEditingStyle.Insert
を返します (挿入ボタンとして動作するように特別に追加されます)。 - CustomizeMoveTarget – ユーザーがセルを移動している間、この省略可能なメソッドからの戻り値により、選択された場所をオーバーライドできます。 つまり、特定の位置でセルを "ドロップ" できないようにすることができます。たとえば、この例では、[(新規追加)] 行の後に行が移動されないようします。
- CanMoveRow – 移動 "ハンドル" を有効にするには true を返し、移動しないようにするには false を返します。 この例では、最後の行は、挿入ボタンとしてのみ機能することが目的であるため、移動 "ハンドル" が非表示になっています。
また、"挿入" 行を追加して、不要になったときにもう一度削除するための 2 つのカスタム メソッドも追加します。 これらは、[編集] および [完了] ボタンから呼び出されます。
- WillBeginTableEditing – [編集] ボタンにタッチすると、テーブルを編集モードにするために
SetEditing
が呼び出されます。 これにより、WillBeginTableEditing メソッドがトリガーされ、テーブルの末尾に "挿入ボタン" として機能する [(新規追加)] 行が表示されます。 - DidFinishTableEditing – [完了] ボタンがタッチされると、編集モードをオフにするために
SetEditing
が呼び出されます。 このコード例では、編集が不要になったときに、テーブルから [(新規追加)] 行が削除されます。
これらのメソッドのオーバーライドは、サンプル ファイル TableEditModeAdd/Code/TableSource.cs に実装されています。
public override UITableViewCellEditingStyle EditingStyleForRow (UITableView tableView, NSIndexPath indexPath)
{
if (tableView.Editing) {
if (indexPath.Row == tableView.NumberOfRowsInSection (0) - 1)
return UITableViewCellEditingStyle.Insert;
else
return UITableViewCellEditingStyle.Delete;
} else // not in editing mode, enable swipe-to-delete for all rows
return UITableViewCellEditingStyle.Delete;
}
public override NSIndexPath CustomizeMoveTarget (UITableView tableView, NSIndexPath sourceIndexPath, NSIndexPath proposedIndexPath)
{
var numRows = tableView.NumberOfRowsInSection (0) - 1; // less the (add new) one
if (proposedIndexPath.Row >= numRows)
return NSIndexPath.FromRowSection(numRows - 1, 0);
else
return proposedIndexPath;
}
public override bool CanMoveRow (UITableView tableView, NSIndexPath indexPath)
{
return indexPath.Row < tableView.NumberOfRowsInSection (0) - 1;
}
次の 2 つのカスタム メソッドは、テーブルの編集モードが有効または無効になっているときに、[(新規追加)] 行の追加と削除を行うために使用されます。
public void WillBeginTableEditing (UITableView tableView)
{
tableView.BeginUpdates ();
// insert the 'ADD NEW' row at the end of table display
tableView.InsertRows (new NSIndexPath[] {
NSIndexPath.FromRowSection (tableView.NumberOfRowsInSection (0), 0)
}, UITableViewRowAnimation.Fade);
// create a new item and add it to our underlying data (it is not intended to be permanent)
tableItems.Add (new TableItem ("(add new)"));
tableView.EndUpdates (); // applies the changes
}
public void DidFinishTableEditing (UITableView tableView)
{
tableView.BeginUpdates ();
// remove our 'ADD NEW' row from the underlying data
tableItems.RemoveAt ((int)tableView.NumberOfRowsInSection (0) - 1); // zero based :)
// remove the row from the table display
tableView.DeleteRows (new NSIndexPath[] { NSIndexPath.FromRowSection (tableView.NumberOfRowsInSection (0) - 1, 0) }, UITableViewRowAnimation.Fade);
tableView.EndUpdates (); // applies the changes
}
最後に、次のコードは、[編集] および [完了] ボタンがタッチされたときに編集モードを有効または無効にするラムダを使用して、それらのボタンのインスタンスを作成します。
done = new UIBarButtonItem(UIBarButtonSystemItem.Done, (s,e)=>{
table.SetEditing (false, true);
NavigationItem.RightBarButtonItem = edit;
tableSource.DidFinishTableEditing(table);
});
edit = new UIBarButtonItem(UIBarButtonSystemItem.Edit, (s,e)=>{
if (table.Editing)
table.SetEditing (false, true); // if we've half-swiped a row
tableSource.WillBeginTableEditing(table);
table.SetEditing (true, true);
NavigationItem.LeftBarButtonItem = null;
NavigationItem.RightBarButtonItem = done;
});
この行挿入 UI パターンはあまり使用されませんが、UITableView.BeginUpdates
と EndUpdates
のメソッドを使用して、任意のテーブル内のセルの挿入または削除をアニメーション化することもできます。 これらのメソッドを使用するためのルールは、BeginUpdates
と EndUpdates
の呼び出し間の RowsInSection
によって返される値の差が、InsertRows
および DeleteRows
メソッドで追加/削除されたセルの正味の数と一致しなければならないということです。 基になるデータソースがテーブル ビューの挿入/削除の数と一致するように変更されていない場合は、エラーが発生します。