ios - UICollectionViews GetSizeForItem() 在调用 collectionView.DequeueReusableCell () 时崩溃

标签 ios iphone xamarin.ios uicollectionview uicollectionviewlayout

我的自定义 UICollectionView 有一个奇怪的行为。

每次打电话

KeyWordsFieldsCell _dummyCellForRendering = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath);

公共(public)覆盖 CGSize GetSizeForItem(UICollectionView collectionView,UICollectionViewLayout 布局,NSIndexPath indexPath)

我的代码在没有错误或 Stacktrace 的情况下崩溃了。

public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)

然而调用

KeyWordsFieldsCell _dummyCellForRendering = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath); 一切正常。

这是我的 UICollectionViews 数据源和委托(delegate)的完整代码。

namespace KeyWordFieldsView
{
    #region CollectionViewDataSource
    public class KeyWordsFieldDataSource : UICollectionViewDataSource
    {

        private readonly UICollectionView keyWordsCollectionView;
        public KeyWordsFieldDataSource (UICollectionView keyWordsCollectionView)
    {
        this.keyWordsCollectionView = keyWordsCollectionView;
    }

    public event EventHandler ContentChangedEvent;

    private List<String> data = new List<String> ();
    public List<String> Data
    {
        get
        {
            return data;
        }
        set
        {
            data = value;
        }
    }


    public override nint GetItemsCount (UICollectionView collectionView, nint section)
    {
        return data.Count;
    }


    public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
    {
        var textCell = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath);
        textCell.initCell ();
        textCell.Text = Data [indexPath.Row];
        textCell.DeleteButtonPressedEvent += HandleDeleteButtonPressedEvent;
        return textCell;
    }



    public void HandleDeleteButtonPressedEvent (object sender, EventArgs a)
    {
        if (sender.GetType () == typeof (KeyWordsFieldsCell))
        {
            var cell = sender as KeyWordsFieldsCell;
            NSIndexPath [] pathsToDelete = { keyWordsCollectionView.IndexPathForCell (cell) };
            if (pathsToDelete [0] != null)
            {
                cell.DeleteButtonPressedEvent -= HandleDeleteButtonPressedEvent;
                Data.RemoveAt (pathsToDelete [0].Row);
                keyWordsCollectionView.DeleteItems (pathsToDelete);
            }
            OnContentChanged (sender, a);
        }
    }

    public void OnContentChanged (object sender, EventArgs ea)
    {
        if (ContentChangedEvent != null)
        {
            ContentChangedEvent (this, ea);
        }
    }

}
#endregion

#region CollectionViewDelegate
class KeyWordsFieldDelegate : UICollectionViewDelegateFlowLayout
{
    public override CGSize GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
    {
        List<String> data = ((KeyWordsFieldDataSource)collectionView.DataSource).Data;
        KeyWordsFieldsCell _dummyCellForRendering = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath);

        _dummyCellForRendering.Text = data [indexPath.Row];
        _dummyCellForRendering.keyWordContainerView.SetNeedsLayout ();
        _dummyCellForRendering.keyWordContainerView.LayoutIfNeeded ();
        double height = Math.Max (_dummyCellForRendering.keyWordLabel.Frame.Height, _dummyCellForRendering.keyWordFieldDeleteButton.Frame.Height);
        double width = Math.Min (_dummyCellForRendering.keyWordContainerView.Frame.Width, collectionView.Bounds.Width);
        _dummyCellForRendering = null;
        return new CGSize (width, height);;
    }

    public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
    {
    }

    public override bool ShouldSelectItem (UICollectionView collectionView, NSIndexPath indexPath)
    {
        return true;
    }

    public override void CellDisplayingEnded (UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath)
    {
        var keyWordCell = cell as KeyWordsFieldsCell;
        keyWordCell.DeleteButtonPressedEvent -= ((KeyWordsFieldDataSource)collectionView.DataSource).HandleDeleteButtonPressedEvent;
    }
}
#endregion


#region left justified cells 
class LeftAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout
{
    nfloat maxCellSpacing = 4;

    public override UICollectionViewLayoutAttributes [] LayoutAttributesForElementsInRect (CGRect rect)
    {

        var attributesForElementsInRect = base.LayoutAttributesForElementsInRect (rect);

        UICollectionViewLayoutAttributes [] newAttributesForElementsInRect = new UICollectionViewLayoutAttributes [attributesForElementsInRect.Count ()];

        var leftMargin = this.SectionInset.Left;

        for (int i = 0; i < attributesForElementsInRect.Count (); i++)
        {
            var attributes = attributesForElementsInRect [i];
            //if Element is first in new Line and already leftaligned or if element is in new line
            if (attributes.Frame.X == leftMargin || attributes.Frame.Y > attributesForElementsInRect[i > 0 ? i-1 : i].Frame.Y)
            {
                leftMargin = this.SectionInset.Left; //reset the leftMargin to left sectionInset.
            }

            CGRect newLeftAlignedFrame = attributes.Frame;
            newLeftAlignedFrame.X = leftMargin;
            attributes.Frame = newLeftAlignedFrame;

            leftMargin += attributes.Size.Width + maxCellSpacing;
            newAttributesForElementsInRect [i] = attributes;
        }
        return newAttributesForElementsInRect;
    }
}
#endregion

这是我的 UICollectionViewCell 的代码

namespace KeyWordFieldsView
{
    public partial class KeyWordsFieldsCell : UICollectionViewCell
    {
        protected KeyWordsFieldsCell (IntPtr handle) : base (handle)
        {
            // Note: this .ctor should not contain any initialization logic.
    }

    public string Text
    {
        get
        {
            return keyWordLabel.Text;
        }
        set
        {
            initCell ();
            keyWordLabel.Text = value;
            keyWordLabel.SizeToFit ();
            SetNeedsDisplay ();
        }
    }
    public UILabel keyWordLabel;
    public UIButton keyWordFieldDeleteButton;
    public UIView keyWordContainerView;

    public static readonly NSString CellId = new NSString ("KeyWordsFieldsCell");
    public event EventHandler DeleteButtonPressedEvent;


    public void initCell () {
        UIColor chipGrey = UIColor.FromRGBA (153, 153, 153, 51);
        ContentView.BackgroundColor = chipGrey;

        ContentView.Layer.CornerRadius = 16;

        if (keyWordContainerView == null)
        {
            keyWordContainerView = new UIView (new CGRect (0, 0, 0, 32));
            keyWordContainerView.TranslatesAutoresizingMaskIntoConstraints = false;
            keyWordContainerView.BackgroundColor = UIColor.Clear;
            ContentView.AddSubview (keyWordContainerView);
        }
        if (keyWordLabel == null)
        {
            keyWordLabel = new UILabel (new CGRect (0, 0, 0, 32));
            keyWordLabel.BackgroundColor = UIColor.Clear;
            UIFont labelFont = UIFont.SystemFontOfSize (14f);
            keyWordLabel.Font = labelFont;
            keyWordLabel.TranslatesAutoresizingMaskIntoConstraints = false;
            keyWordLabel.LineBreakMode = UILineBreakMode.MiddleTruncation;
            keyWordContainerView.AddSubview (keyWordLabel);

        }
        if (keyWordFieldDeleteButton == null)
        {
            keyWordFieldDeleteButton = UIButton.FromType (UIButtonType.Custom);
            keyWordFieldDeleteButton.Frame = new CGRect (0, 0, 32, 32);
            keyWordFieldDeleteButton.SetImage (UIImage.FromBundle ("remove-icon"), UIControlState.Normal);

            keyWordFieldDeleteButton.BackgroundColor = UIColor.Clear;
            keyWordFieldDeleteButton.TouchUpInside += DeleteButtonPressed;
            keyWordFieldDeleteButton.TranslatesAutoresizingMaskIntoConstraints = false;
            keyWordContainerView.AddSubview (keyWordFieldDeleteButton);
        }
        else {
            //Add ButtonEvent in Case of Reuse
            keyWordFieldDeleteButton.TouchUpInside -= DeleteButtonPressed;
            keyWordFieldDeleteButton.TouchUpInside += DeleteButtonPressed;
        }


        var cvDictionary = NSDictionary.FromObjectsAndKeys (new NSObject [] { keyWordContainerView }, new NSObject [] { new NSString ("kwcv") });
        ContentView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("H:|[kwcv]|", 0, new NSDictionary (), cvDictionary));
        ContentView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("V:|[kwcv]|", 0, new NSDictionary (), cvDictionary));
        keyWordContainerView.SetContentHuggingPriority (249, UILayoutConstraintAxis.Vertical);
        keyWordContainerView.SetContentCompressionResistancePriority (749, UILayoutConstraintAxis.Vertical);

        var viewsDictionary = NSDictionary.FromObjectsAndKeys (new NSObject [] { keyWordLabel, keyWordFieldDeleteButton }, new NSObject [] { new NSString ("kwlbl"), new NSString ("kwbtn") });
        keyWordContainerView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("H:|-[kwlbl][kwbtn(==32)]|", 0, new NSDictionary (), viewsDictionary));
        keyWordContainerView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("V:|[kwbtn(==32)]|", 0, new NSDictionary (), viewsDictionary));
        keyWordContainerView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("V:|[kwlbl]|", 0, new NSDictionary (), viewsDictionary));
        keyWordFieldDeleteButton.SetContentHuggingPriority (249, UILayoutConstraintAxis.Vertical);
        keyWordFieldDeleteButton.SetContentCompressionResistancePriority (751, UILayoutConstraintAxis.Vertical);
        keyWordLabel.SetContentHuggingPriority (249, UILayoutConstraintAxis.Vertical);
        keyWordLabel.SetContentCompressionResistancePriority (749, UILayoutConstraintAxis.Vertical);
    }

    //[Export ("initWithFrame:")]
    //public KeyWordsFieldsCell (CGRect frame) : base (frame)
    //{
    //  initCell ();
    //}

    public override void LayoutSubviews ()
    {
        base.LayoutSubviews ();
    }


    public void DeleteButtonPressed (object sender, EventArgs ea)
    {
        ((UIButton)sender).TouchUpInside -= DeleteButtonPressed;
        OnDeleteButtonPressed (sender, ea);
    }

    void OnDeleteButtonPressed (object sender, EventArgs ea)
    {
        if (DeleteButtonPressedEvent != null)
        {
            DeleteButtonPressedEvent (this, ea);
        }
    }
}

这是初始化 UICollectionView 的地方:

if (CollectionView != null && CollectionView.DataSource == null)
        {
            CollectionView.RegisterClassForCell (typeof (KeyWordsFieldsCell), KeyWordsFieldsCell.CellId);
            CollectionView.TranslatesAutoresizingMaskIntoConstraints = false;
            CollectionView.SetCollectionViewLayout (new LeftAlignedCollectionViewFlowLayout (), false);

            KeyWordsFieldDataSource Source = new KeyWordsFieldDataSource (CollectionView);
            if (data != null)
            {
                Source.Data = data;
            }
            CollectionView.DataSource = Source;
            KeyWordsFieldDelegate keyWordsDelegate = new KeyWordsFieldDelegate ();
            CollectionView.Delegate = keyWordsDelegate;

            (CollectionView.CollectionViewLayout as UICollectionViewFlowLayout).MinimumLineSpacing = 4;
            (CollectionView.CollectionViewLayout as UICollectionViewFlowLayout).MinimumInteritemSpacing = 2;
            //CollectionViewHeightConstraint.Constant = CollectionView.CollectionViewLayout.CollectionViewContentSize.Height;
        }

希望有人能帮忙,因为这是一个相当令人沮丧的问题。

您好, 小牛

最佳答案

对于遇到同样问题的任何人。

坦率地说,UITableView 和 UICollectionView 之间只是行为不同。 在 UITableView 中,完全可以在 getHeightForRow() 中调用 dequeueReusableCellWithReuseIdentifier() 来获取用于高度计算的单元格,在 UICollectionView 中的 sizeForItemAtIndexPath 中调用它会导致无限循环,从而导致应用程序崩溃。

感谢@Markus Rautopuro 用他的 Answer 为我指明了正确的方向

我现在通过计算单元格中组件的大小来计算单元格的高度。这工作得很好并且需要更少的资源,因为我不需要构建一个完整的单元格,而只需要构建总高度的项目。

关于ios - UICollectionViews GetSizeForItem() 在调用 collectionView.DequeueReusableCell () 时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42162287/

相关文章:

ios - MagicalRecord 几个截断, "then cannot find data for a temporary oid"

ios - 数组中的数组(二维数组?)

iphone - 使用正则表达式在 Xcode 中查找/替换

iphone - 串行写入不起作用

c# - 如何在 Monotouch 中绑定(bind)扩展方法?

memory-leaks - 为什么即使是非常简单的应用程序,MonoTouch 也会导致大量内存泄漏(如 Instruments 所报告的那样)?

objective-c - 求计算器中根底的幂

ios - 将 SBJson 与 facebook sdk 静态库一起使用

iphone - iOS 开发 : What are some ways I can troubleshoot a lag issue in my game that occurs 15 - 30 minutes after playing it?

xamarin - 示例 Xamarin 应用程序在 iPhone 模拟器上崩溃