我的自定义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;
}
希望有人可以帮忙,因为这是一个相当令人沮丧的问题。
问候,
小牛
最佳答案
因此,对于偶然遇到同一问题的任何人。
坦白说,UITableView和UICollectionView之间只有一个不同的行为。
在UITableView中完全可以在getHeightForRow()中调用dequeueReusableCellWithReuseIdentifier()来获取用于高度计算的单元格,在UICollectionView中的sizeForItemAtIndexPath中调用它会导致无限期循环,从而导致应用程序崩溃。
感谢@Markus Rautopuro,用他的Answer向我指出正确的方向
我现在通过计算单元中组件的大小来计算单元的高度。这样做效果很好,需要的资源也较少,因为我不需要构建完整的单元,而只需构建高度就可以的项目。