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

时间:2017-02-10 14:53:13

标签: ios iphone xamarin.ios uicollectionview uicollectionviewlayout

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

每次我打电话

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

public override CGSize GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)

我的代码崩溃,没有错误或Stacktrace。

public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)

然而是

的召唤

KeyWordsFieldsCell _dummyCellForRendering = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath); 工作正常。

以下是我的UICollectionViews数据源和代理的完整代码。

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

希望有人可以提供帮助,因为这是一个相当令人沮丧的问题。

电贺, MAV

1 个答案:

答案 0 :(得分:0)

对于那些偶然遇到同样问题的人来说。

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

感谢@Markus Rautopuro指出我正确的方向Answer

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