使用Xamarin.iOS在单击处理程序中添加UICollectionViewCell中的按钮

时间:2013-05-31 12:22:43

标签: xamarin.ios uicollectionview

我正在将之前使用UITableView的屏幕重写为UICollectionView。但我在单元格内的按钮上遇到单击处理程序时遇到问题。

collectionView.RegisterNibForCell (DocumentViewCell.Nib, docCellId);    
...

public override UICollectionViewCell GetCell (UICollectionView collectionView, MonoTouch.Foundation.NSIndexPath indexPath)
{
    var docCell = (DocumentViewCell)collectionView.DequeueReusableCell (docCellId, indexPath);                
    docCell.BtnDelete.Hidden = !EditMode;
    docCell.BtnDelete.TouchUpInside += delegate(object sender, EventArgs e) {
        Logging.Debug("Crashes before if reaches here");
    };
    return docCell;
}

我知道单元格开始重复使用,当发生这种情况时,这很可能不会起作用,但现在当按下删除按钮时,它只会在列表中只有一个元素时立即崩溃。一切都很好,直到我按下按钮然后我得到下面的堆栈跟踪。基于我的UITableView代码几乎完全相同,我认为没有理由发生这种情况。

有没有人使用基于Nib的CollectionView单元格做到这一点?非常感谢任何帮助!

堆栈跟踪:

 at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x0009f, 0xffffffff>
 at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
 at SalesApp.Application.Main (string[]) [0x00000] in /MyApp/Main.cs:18
 at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00050, 0xffffffff>

Native stacktrace:

0   SalesApp                            0x0009a85c mono_handle_native_sigsegv + 284
1   SalesApp                            0x0000e138 mono_sigsegv_signal_handler + 248
2   libsystem_c.dylib                   0x990a78cb _sigtramp + 43
3   ???                                 0xffffffff 0x0 + 4294967295
4   UIKit                               0x01990258 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 61
5   UIKit                               0x01a51021 -[UIControl sendAction:to:forEvent:] + 66
6   UIKit                               0x01a5157f -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 578
7   UIKit                               0x01a506e8 -[UIControl touchesEnded:withEvent:] + 546
8   UIKit                               0x01c541d3 _UIGestureRecognizerUpdate + 7407
9   CoreFoundation                      0x03ecbafe __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30

更新

此代码可以正常使用

public override UICollectionViewCell GetCell (UICollectionView collectionView, MonoTouch.Foundation.NSIndexPath indexPath)
{
    var docCell = (DocumentViewCell)collectionView.DequeueReusableCell (DocumentViewCell.Key, indexPath);
    docCell.BtnDelete.Hidden = !EditMode;
    docCell.BtnDelete.TouchUpInside -= HandleTouchUpInside;
    docCell.BtnDelete.TouchUpInside += HandleTouchUpInside;
    return docCell;
}

void HandleTouchUpInside (object sender, EventArgs e)
{
    Logging.Debug("No crash");
}

2 个答案:

答案 0 :(得分:4)

问题是,您没有从docCell方法返回的GetCell实例的托管引用。这意味着GC可以随时收集它。

如果再次需要,它将重新表面到托管世界(使用IntPtr构造函数)。它将是(重新使用的)原生实例,但 托管实例。发生崩溃是因为事件指向旧的托管实例(已收集)。

简单的解决方案是在视图存在的同时保持创建的单元格的缓存。这将确保GC在使用之前不会收集(管理的)单元格。有几个答案显示UITableView

注意:我认为我们在最新版本的Xamarin.iOS中修复(隐藏)了这个。也许它仅适用于UITableView!?!需要检查一下......

答案 1 :(得分:1)

我猜DequeueReusableCell会返回null,因为它是第一个被绘制的单元格。

您的代码应该是这样的:

public override UICollectionViewCell GetCell (UICollectionView collectionView, MonoTouch.Foundation.NSIndexPath indexPath)
{
    var docCell = (DocumentViewCell)collectionView.DequeueReusableCell (docCellId, indexPath);
    if (docCell == null)
        docCell = new UICollectionViewCell (...);
    docCell.BtnDelete.TouchUpInside += delegate(object sender, EventArgs e) {
        Logging.Debug("Crashes before if reaches here");
    };
    return docCell;
}

用创建单元格的任何内容替换省略号(...)