繁体   English   中英

删除TableView中的行后,如何更新indexPath?

[英]How can I update my indexPath after deleting a Row in a TableView?

我是Xamarin新手,正在尝试制作一些基本的todolist iOS应用。

在使用TableViews挣扎了一段时间之后,我现在有了一个TableSource和包含UISwitch自定义单元格。

我的目标是,当用户激活UISwitch ,将为TableView删除单元格。 为此,我在自定义单元格中创建了一些自定义事件。

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);
    }
}

当用户激活UISwitch时,将调用DeleteTodoEvent 这是我的表源:

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);
    }
}

基本上,只要用户单击UISwitch就会调用DeleteTodoHandler 首先它会正确删除该单元格。 但是,indexPath永远不会更新。

我的意思是:想象一下,我的TodoList中有3个单元格。

Cell1-> indexPath.Row = 0 Cell2-> indexPath.Row = 1 Cell3-> indexPath.Row = 2

如果删除第二个单元格Cell2,则Cell3的indexPath应该= = 1而不是2。事实并非如此。 这意味着如果我之后尝试删除Cell3,

TableItems.RemoveAt(indexPath.Row) 

会尝试从列表中删除项目3,这导致我们出现异常,因为列表中只有2个项目。

我正在遵循我在Xamarin Doc中找到的用于删除TableView中的行的示例,但就我而言,它显然不起作用。 indexPath.Row是只读的,从TableView中删除项目后,我该怎么做才能正确更新indexPath?

您应该使用同时包含UITableView和自定义UITableSource对象的ViewController来处理删除事件。

然后,您可以从UITableView中获得方法单击的正确行,如下所示:

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

并同时从数据列表和用户界面中删除该行的数据。

这是我为您编写的示例代码,请看一下:

这是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();
            };
        }
    }

希望它能对您有所帮助。

如果您仍然需要建议,请在此处给我留言。

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

我只是建议您将所有逻辑代码移到ViewController中,因为根据MVC模型,它应该在Controller层中。

而且,如果您想通过调用一些API或从本地数据库加载数据来管理数据,这将更加方便。

当然,您可以将delete事件放在TableSource中,只需要从控制器中删除该事件并将其放入TableSource中即可,例如:

ViewController.cs:

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();
//          };
        }

在TableSource.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行,而您删除了第二行,则它可以工作。 但是,当您尝试删除第三行时(现在是第二行,因为您只删除了第二行),您将得到一个例外,因为您没有3行或索引越界,因为此单元格的IndexPath是仍然是2(正确的是1)。

因此,您需要使用UITableView的方法“ IndexPathForCell”在删除事件处理程序中计算索引,就像我上面提到的那样。 它将始终获得正确的索引。

------------------关于您的新问题------------------

您犯的错误是关于delete事件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];

这将导致重复绑定问题,因为表单元已被重用,因此当您尝试刷新/插入/删除甚至滚动UITableView时,将触发“ GetCell”方法,因此当您尝试调用单元的delete事件时,如果将调用多个委托,第一个委托正在工作,并且如果您至少有2个委托,它将导致遇到的问题。

效果类似于此屏幕截图: 错误截图 重现步骤:1为tableview添加一个新项目; 2删除刚添加的项目; 3再次添加新项目; 4删除刚添加的项目;

因为那里有2位代表,所以引发了异常。

最后,我将完整的示例代码上传到了我的GitHub,如果需要,请下载它。

UITalbViewManagement示例代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM