2016-09-16 21 views
0

私はXamarinを初めて使い、いくつかの基本的なtodolist iOSアプリを作ろうとしています。TableViewで行を削除した後でindexPathを更新するにはどうすればよいですか?

TableViewsでしばらくの間苦労した後、私は今TableSourceとUISwitchを含むカスタムセルを持っています。

私の目標は、ユーザーがUISwitchをアクティブにすると、TableViewのセルが削除されるということです。これを行うために、カスタムセルにカスタムイベントを作成しました。ユーザーがUISwitchをアクティブ化するとき

public partial class TodoTableViewCell : UITableViewCell 
{ 
    public delegate void DeleteTodoEventHandler(UITableView tableView, NSIndexPath indexPath); 
    public event DeleteTodoEventHandler DeleteTodo; 

    partial void DeleteTodoEvent(UISwitch sender) 
    { 
     if (DeleteTodo != null) 
      DeleteTodo(null, new NSIndexPath()); 
    } 

    public TodoTableViewCell(IntPtr p):base(p) 
    { 

    } 

    public TodoTableViewCell(string reuseIdentifier) : base(UITableViewCellStyle.Default, reuseIdentifier) 
    { 

    } 

    public void UpdateCell(TodoItem Item) 
    { 
     TodoLabel.Text = Item.Name; 
     DateLabel.Text = Formating.formatDate(Item.Date); 
     HourLabel.Text = Formating.formatTime(Item.Date); 
    } 
} 

DeleteTodoEventが呼び出されます。ここに私のtablesourceです:

public class TableSource : UITableViewSource 
{ 

    List<TodoItem> TableItems; 
    string CellIdentifier = "TodoCell"; 

    ... 

    public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) 
    { 
     TodoTableViewCell cell = tableView.DequeueReusableCell(CellIdentifier) as TodoTableViewCell; 
     TodoItem item = TableItems[indexPath.Row]; 

     //---- if there are no cells to reuse, create a new one 
     if (cell == null) 
     { 
      cell = new TodoTableViewCell(CellIdentifier); 
     } 

     cell.DeleteTodo += (table, index) => DeleteTodoHandler(tableView, indexPath); 

     cell.UpdateCell(item); 

     return cell; 
    } 

    ... 

    private void DeleteTodoHandler(UITableView tableView, NSIndexPath indexPath) 
    { 
     TableItems.RemoveAt(indexPath.Row); 
     // delete the row from the table 
     tableView.DeleteRows(new NSIndexPath[] { indexPath }, UITableViewRowAnimation.Fade); 
    } 
} 

は基本的には、DeleteTodoHandlerが呼ばれるたびにUISwitch上のユーザーがクリック。最初にセルを正しく削除します。ただし、indexPathは決して更新されません。

これは私が意味することです:自分のTodoListに3つのセルがあるとします。

セル1 - > indexPath.Row = 0 セル2 - > indexPath.Row = 1 CELL3 - > indexPath.Row = 2

Iは、第1、セル2、indexPathからを削除した場合Cell3は、= 1でなく2でなければなりません。これは当てはまりません。これは私が、その後CELL3を削除しようとする場合

TableItems.RemoveAt(indexPath.Row) 

は、リスト内の唯一の2つの項目があるので例外に私たちをリードして、リストから項目3を削除しようとすることを意味します。

私はXamarin DocでTableView内の行を削除する例を見ていますが、私の場合は明らかに機能しません。 indexPath.Rowは読み取り専用ですが、TableViewからアイテムを削除した後にindexPathを正しく更新するにはどうすればよいですか?

答えて

0

削除イベントを処理するには、UITableViewとカスタムUITableSourceオブジェクトの両方を含むViewControllerを使用する必要があります。

その後、あなたはこのようのUITableViewからの方法によりクリックされた正しい行を取得することができます:

Foundation.NSIndexPath indexPath = tableView.IndexPathForCell(cell); 

そして、あなたのデータリストと同じ時間であなたのUIから行のデータを削除します。

これは私があなたのために書いたサンプルコードで、見てみましょう:

これはViewControllerを次のとおりです。

public override void ViewDidLoad() 
     { 
      //Create a custom table source 
      MyTableSource mySource = new MyTableSource(); 

      //Create a UITableView 
      UITableView tableView = new UITableView(); 
      CGRect tbFrame = this.View.Bounds; 
      tbFrame.Y += 20; 
      tbFrame.Height -= 20; 
      tableView.Frame = tbFrame; 
      tableView.Source = mySource; 
      tableView.RowHeight = 50; 
      this.Add (tableView); 

      //Handle delete event 
      mySource.DeleteCell += (cell) => { 
       //Get the correct row 
       Foundation.NSIndexPath indexPath = tableView.IndexPathForCell(cell); 
       //Remove data from source list 
       mySource.RemoveAt(indexPath.Row); 
       //Remove selected row from UI 
       tableView.BeginUpdates(); 
       tableView.DeleteRows(new Foundation.NSIndexPath[]{indexPath},UITableViewRowAnimation.Fade); 
       tableView.EndUpdates(); 
      }; 
     } 

このMyTableSource.cs:

public delegate void DeleteCellHandler(UITableViewCell cell); 
     public event DeleteCellHandler DeleteCell; 

     private string CellID = "MyTableCell"; 
     private List<string> tableItems; 

     public MyTableSource() 
     { 
      tableItems = new List<string>(); 
      for (int i = 0; i < 10; i++) { 
       tableItems.Add ("Test Cell " + i.ToString()); 
      } 
     } 

     public void RemoveAt(int row) 
     { 
      tableItems.RemoveAt (row); 
     } 

     #region implement from UITableViewSource 

     public override nint RowsInSection (UITableView tableview, nint section) 
     { 
      return tableItems.Count; 
     } 

     public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath) 
     { 
      MyTableCell cell = tableView.DequeueReusableCell (CellID) as MyTableCell; 
      if (null == cell) { 
       //Init custom cell 
       cell = new MyTableCell (UITableViewCellStyle.Default, CellID); 
       //Bind delete event 
       cell.DeleteCell += delegate { 
        if(null != DeleteCell) 
         DeleteCell(cell); 
       }; 
      } 
      cell.Title = tableItems [indexPath.Row]; 
      return cell; 
     } 

     #endregion 

これはMyTableCellです。cs:

public class MyTableCell : UITableViewCell 
    { 
     public delegate void DeleteCellHandler(); 
     public event DeleteCellHandler DeleteCell; 

     UILabel lbTitle = new UILabel(); 
     UIButton btnDelete = new UIButton (UIButtonType.System); 

     public string Title{ 
      get{ 
       return lbTitle.Text; 
      } 
      set{ 
       lbTitle.Text = value; 
      } 
     } 

     public MyTableCell (UITableViewCellStyle style, string reuseID) : base(style,reuseID) 
     { 
      lbTitle.Text = ""; 
      lbTitle.Frame = new CoreGraphics.CGRect (0, 0, 100, 50); 
      this.AddSubview (lbTitle); 

      btnDelete.SetTitle ("Delete", UIControlState.Normal); 
      btnDelete.Frame = new CoreGraphics.CGRect (120, 0, 50, 50); 
      this.AddSubview (btnDelete); 
      btnDelete.TouchUpInside += delegate { 
       if(null != DeleteCell) 
        DeleteCell(); 
      }; 
     } 
    } 

ご希望の場合はお手数ですが、

アドバイスが必要な場合は、ここにメッセージを残してください。

------------------------------新--------------- ---------------------

MVCモデルによれば、Controllerレイヤー内にあるため、すべてのロジックコードをViewControllerに移動することをお勧めします。

APIを呼び出したり、ローカルデータベースからデータを読み込んだりすることによってデータを管理したい場合は、より便利になります。あなたはTableSourceであなたの削除イベントを置くことができます原因

、ちょうどコントローラからイベントを削除し、TableSourceにそれを置く、のようにする必要があります。

ViewController.cs:TableSourceで

public override void ViewDidLoad() 
     { 
      //Create a custom table source 
      MyTableSource mySource = new MyTableSource(); 

      //Create a UITableView 
      UITableView tableView = new UITableView(); 
      CGRect tbFrame = this.View.Bounds; 
      tbFrame.Y += 20; 
      tbFrame.Height -= 20; 
      tableView.Frame = tbFrame; 
      tableView.Source = mySource; 
      tableView.RowHeight = 50; 
      this.Add (tableView); 

//   //Handle delete event 
//   mySource.DeleteCell += (cell) => { 
//    //Get the correct row 
//    Foundation.NSIndexPath indexPath = tableView.IndexPathForCell(cell); 
//    //Remove data from source list 
//    mySource.RemoveAt(indexPath.Row); 
//    //Remove selected row from UI 
//    tableView.BeginUpdates(); 
//    tableView.DeleteRows(new Foundation.NSIndexPath[]{indexPath},UITableViewRowAnimation.Fade); 
//    tableView.EndUpdates(); 
//   }; 
     } 

。 CSは、このいずれかのようgetcellを方法を変更:

public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath) 
     { 
      MyTableCell cell = tableView.DequeueReusableCell (CellID) as MyTableCell; 
      if (null == cell) { 
       //Init custom cell 
       cell = new MyTableCell (UITableViewCellStyle.Default, CellID); 
       //Bind delete event 
       cell.DeleteCell += delegate { 
//     if(null != DeleteCell) 
//      DeleteCell(cell); 
        //Get the correct row 
        Foundation.NSIndexPath dIndexPath = tableView.IndexPathForCell(cell); 
        int deleteRowIndex = dIndexPath.Row; 
        Console.WriteLine("deleteRowIndex = "+deleteRowIndex); 
        //Remove data from source list 
        RemoveAt(deleteRowIndex); 
        //Remove selected row from UI 
        tableView.BeginUpdates(); 
        tableView.DeleteRows(new Foundation.NSIndexPath[]{dIndexPath},UITableViewRowAnimation.Fade); 
        tableView.EndUpdates(); 
       }; 
      } 
      cell.Title = tableItems [indexPath.Row]; 
      return cell; 
     } 

あなたが作った唯一の間違いは、あなたのカスタムセルの削除イベントハンドル内の任意のパラメータを追加するべきではないです。

テーブルからセルを削除すると、他のセルは自分のIndexPathを更新しないためです。

たとえば、3つの行があり、2番目の行を削除すると、その行が機能します。 しかし、3番目の行を削除しようとすると(2番目の行を削除するだけで2番目の行です)、3行もインデックスの境界もないという例外が発生します。このセルのIndexPathまだ2です(正しい1は1です)。

上記のように、UITableViewのメソッド "IndexPathForCell"を使用して、削除イベントハンドラのインデックスを計算する必要があります。常に正しいインデックスを取得します。

--------------------

あなたが作った間違いを約イベントのinitを削除しているセルを構成したときに私がやったように、あなたは、それを初期化する必要があります。

public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath) 
     { 
      MyTableCell cell = tableView.DequeueReusableCell (CellID) as MyTableCell; 
      if (null == cell) { 
       cell = new MyTableCell (UITableViewCellStyle.Default, CellID); 
       //Bind delete event 
       cell.DeleteCell += delegate { 
        //Delete stuff    
       }; 
      } 
      cell.Title = tableItems [indexPath.Row]; 
      return cell; 
     } 

をあなただけのあなたのようにそれを使用する場合:

if (null == cell) { 
    cell = new MyTableCell (UITableViewCellStyle.Default, CellID); 
} 
//Bind delete event 
cell.DeleteCell += delegate { 
    //Delete stuff    
}; 
cell.Title = tableItems [indexPath.Row]; 

それが結合重複が発生します問題は、あなたの表のセルが再利用されているので、あなたが/挿入/削除またはscあなたのUITableViewをロールすると、 "GetCell"メソッドがトリガーされるので、セルの削除イベントを呼び出そうとすると、複数のデリゲートが呼び出され、最初のものが動作し、少なくとも2人のデリゲートがいる場合、あなたが会いました。

効果はこのスクリーンショットのようなものです: Error screenshot 再現手順: 1は、テーブルビューのために新しいアイテムを追加します。 2追加したばかりの項目を削除します。 3新しいアイテムを再度追加します。 4追加したばかりの項目を削除します。

デリゲートが2人いるため、例外がスローされました。

最後に、完全なサンプルコードをGitHubにアップロードして、必要に応じてダウンロードしました。

UITalbViewManagement Sample code

+0

こんにちは、すべての まず、あなたが答えてくれてありがとう。私はこの角度での問題を見たことがないし、実際はもっと論理的であるように見える。 しかし、私は間違いなく(Console.WriteLineによって確認された)TableSource内のDeleteCellに入っていますが、コントローラのViewDidLoad関数内のDeleteCellを決して通過しません。 理由が分かりますか? – Pictar

+0

自分の答えを修正しました。あなたの問題を解決するはずです。@ Pictar –

+0

削除がうまくいきました。ありがとうございました:)残っている唯一の問題はTodoの追加です。私はViewDidLoadでの追加のためのビューを作成し、正しく追加のイベントをキャッチしています。私がアプリケーションを起動してTodoを追加すると正しく表示されますが、それを削除しようとすると** dIndexPath **の周囲に** NullException **が発生します。 – Pictar

関連する問題