如何使用 UICollectionViewCompositionalLayout 和多种单元格类型创建自动调整大小的单元格?
How to create auto sized cells using UICollectionViewCompositionalLayout and multiple cell types?
我正在尝试在 iOS13 上使用新的 UICollectionViewCompositionalLayout
。我想显示两种不同的单元格类型,但我希望布局系统自动调整单元格的大小。我想利用 NSCollectionLayoutDimension.estimated
认为自动大小会为我处理。
我正在尝试使用 Xamarin 执行此操作,但 Swift 编码人员应该能够很好地阅读以下代码。我不太擅长自动布局和动态单元格大小,但到目前为止我能够查看在线资源。几乎每个 UICollectionViewCompositionalLayout
示例似乎都在使用单个单元格模板。
public class MyViewController : UIViewController
{
private UICollectionView _collectionView;
public static List<BaseModel> Models = new List<BaseModel>();
public override void ViewDidLoad()
{
base.ViewDidLoad();
PopulateModels();
_collectionView = new UICollectionView(View.Frame, GetUiCollectionViewLayout())
{
BackgroundColor = UIColor.Brown,
DataSource = new MyViewUICollectionViewDataSource(),
ContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.Never,
TranslatesAutoresizingMaskIntoConstraints = false,
ShowsVerticalScrollIndicator = false,
ShowsHorizontalScrollIndicator = false
};
_collectionView.RegisterClassForCell(typeof(MyViewUICollectionViewCell), MyViewUICollectionViewCell.ReuseIdentifier);
_collectionView.RegisterClassForCell(typeof(MyViewUICollectionViewCell2), MyViewUICollectionViewCell2.ReuseIdentifier);
View.AddSubview(_collectionView);
NSLayoutConstraint.ActivateConstraints(new[]
{
_collectionView.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor),
_collectionView.BottomAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.BottomAnchor),
_collectionView.LeftAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.LeftAnchor),
_collectionView.RightAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.RightAnchor)
});
}
private static void PopulateModels()
{
for (var i = 0; i < 100; i++)
{
BaseModel baseModel;
if (i % 2 == 0)
{
baseModel = new Model1
{
UIColor = UIColor.Red
};
}
else
{
baseModel = new Model2
{
Text = "Item " + i
};
}
Models.Add(baseModel);
}
}
private UICollectionViewLayout GetUiCollectionViewLayout()
{
var layoutSize = NSCollectionLayoutSize.Create(NSCollectionLayoutDimension.CreateFractionalWidth(1), NSCollectionLayoutDimension.CreateEstimated(200));
var item = NSCollectionLayoutItem.Create(layoutSize);
var group = NSCollectionLayoutGroup.CreateHorizontal(layoutSize: layoutSize, subitem: item, count: 1);
var section = NSCollectionLayoutSection.Create(group);
// this is what you need for content inset
section.ContentInsets = new NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5);
// this is spacing between items
section.InterGroupSpacing = 5;
var layout = new UICollectionViewCompositionalLayout(section);
return layout;
}
}
public class BaseModel
{
}
public class Model1 : BaseModel
{
public UIColor UIColor { get; set; }
}
public class Model2 : BaseModel
{
public string Text { get; set; }
}
public class MyViewUICollectionViewDataSource : UICollectionViewDataSource
{
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var model = MyViewController.Models[(int) indexPath.Item];
switch (model)
{
case Model1 model1:
{
var cell = collectionView.DequeueReusableCell(MyViewUICollectionViewCell.ReuseIdentifier, indexPath) as MyViewUICollectionViewCell;
cell.ColorView.BackgroundColor = model1.UIColor;
return cell;
}
case Model2 model2:
{
var cell2 = collectionView.DequeueReusableCell(MyViewUICollectionViewCell2.ReuseIdentifier, indexPath) as MyViewUICollectionViewCell2;
cell2.Label.Text = model2.Text;
return cell2;
}
default:
throw new Exception();
}
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
return MyViewController.Models.Count;
}
}
public class MyViewUICollectionViewCell : UICollectionViewCell
{
public const string ReuseIdentifier = "MyViewUICollectionViewCell";
public UIView ColorView { get; }
[Export("initWithFrame:")]
public MyViewUICollectionViewCell(CGRect frame) : base(frame)
{
var container = new UIView
{
BackgroundColor = UIColor.White,
TranslatesAutoresizingMaskIntoConstraints = false
};
container.Layer.CornerRadius = 5;
ContentView.AddSubview(container);
container.TopAnchor.ConstraintEqualTo(ContentView.TopAnchor).Active = true;
container.LeftAnchor.ConstraintEqualTo(ContentView.LeftAnchor).Active = true;
container.BottomAnchor.ConstraintEqualTo(ContentView.BottomAnchor).Active = true;
container.RightAnchor.ConstraintEqualTo(ContentView.RightAnchor).Active = true;
ColorView = new UIView
{
TranslatesAutoresizingMaskIntoConstraints = false
};
ColorView.Layer.CornerRadius = 10;
container.AddSubview(ColorView);
ColorView.CenterXAnchor.ConstraintEqualTo(container.CenterXAnchor).Active = true;
ColorView.CenterYAnchor.ConstraintEqualTo(container.CenterYAnchor).Active = true;
ColorView.WidthAnchor.ConstraintEqualTo(20).Active = true;
ColorView.HeightAnchor.ConstraintEqualTo(50).Active = true;
}
}
public class MyViewUICollectionViewCell2 : UICollectionViewCell
{
public const string ReuseIdentifier = "MyViewUICollectionViewCell2";
public UILabel Label { get; }
[Export("initWithFrame:")]
public MyViewUICollectionViewCell2(CGRect frame) : base(frame)
{
var container = new UIView
{
BackgroundColor = UIColor.White,
TranslatesAutoresizingMaskIntoConstraints = false
};
container.Layer.CornerRadius = 5;
ContentView.AddSubview(container);
container.TopAnchor.ConstraintEqualTo(ContentView.TopAnchor).Active = true;
container.LeftAnchor.ConstraintEqualTo(ContentView.LeftAnchor).Active = true;
container.BottomAnchor.ConstraintEqualTo(ContentView.BottomAnchor).Active = true;
container.RightAnchor.ConstraintEqualTo(ContentView.RightAnchor).Active = true;
Label = new UILabel
{
TranslatesAutoresizingMaskIntoConstraints = false
};
container.AddSubview(Label);
Label.CenterXAnchor.ConstraintEqualTo(container.CenterXAnchor).Active = true;
Label.CenterYAnchor.ConstraintEqualTo(container.CenterYAnchor).Active = true;
//Label.WidthAnchor.ConstraintEqualTo(20).Active = true;
//Label.HeightAnchor.ConstraintEqualTo(20).Active = true;
}
}
如您所见,问题是两种细胞类型使用相同的估计高度 200。我不确定我做错了什么。我希望两种单元格类型都自动调整大小以适合其内容,而不是使用额外的 space 来达到 200。我尝试使用较小的估计大小,例如 10,但这并没有改变任何东西。应如何更改代码以具有有效的自动调整大小功能?
这取决于您在 CollectionViewCell
中设置的约束条件,请不要给标签指定固定大小,否则它不会自动调整大小,在 MyViewUICollectionViewCell2 中,将标签的约束条件更改为:
public class MyViewUICollectionViewCell2 : UICollectionViewCell
{
public const string ReuseIdentifier = "MyViewUICollectionViewCell2";
public UILabel Label { get; }
[Export("initWithFrame:")]
public MyViewUICollectionViewCell2(CGRect frame) : base(frame)
{
var container = new UIView
{
BackgroundColor = UIColor.White,
TranslatesAutoresizingMaskIntoConstraints = false
};
container.Layer.CornerRadius = 5;
ContentView.AddSubview(container);
container.TopAnchor.ConstraintEqualTo(ContentView.TopAnchor).Active = true;
container.LeftAnchor.ConstraintEqualTo(ContentView.LeftAnchor).Active = true;
container.BottomAnchor.ConstraintEqualTo(ContentView.BottomAnchor).Active = true;
container.RightAnchor.ConstraintEqualTo(ContentView.RightAnchor).Active = true;
Label = new UILabel
{
Lines = 0;
TranslatesAutoresizingMaskIntoConstraints = false
};
container.AddSubview(Label);
Label.TopAnchor.ConstraintEqualTo(container.TopAnchor).Active = true;
Label.LeftAnchor.ConstraintEqualTo(container.LeftAnchor).Active = true;
Label.BottomAnchor.ConstraintEqualTo(container.BottomAnchor).Active = true;
Label.RightAnchor.ConstraintEqualTo(container.RightAnchor).Active = true;
//Label.CenterXAnchor.ConstraintEqualTo(container.CenterXAnchor).Active = true;
//CenterYAnchor.ConstraintEqualTo(container.CenterYAnchor).Active = true;
//Label.WidthAnchor.ConstraintEqualTo(20).Active = true;
//Label.HeightAnchor.ConstraintEqualTo(20).Active = true;
}
}
对于MyViewUICollectionViewCell1
中的colorView
,如果您知道colorView
的大小,您应该将container
的大小指定为与[=相同的大小12=], 不会长成不同文字的label.
我正在尝试在 iOS13 上使用新的 UICollectionViewCompositionalLayout
。我想显示两种不同的单元格类型,但我希望布局系统自动调整单元格的大小。我想利用 NSCollectionLayoutDimension.estimated
认为自动大小会为我处理。
我正在尝试使用 Xamarin 执行此操作,但 Swift 编码人员应该能够很好地阅读以下代码。我不太擅长自动布局和动态单元格大小,但到目前为止我能够查看在线资源。几乎每个 UICollectionViewCompositionalLayout
示例似乎都在使用单个单元格模板。
public class MyViewController : UIViewController
{
private UICollectionView _collectionView;
public static List<BaseModel> Models = new List<BaseModel>();
public override void ViewDidLoad()
{
base.ViewDidLoad();
PopulateModels();
_collectionView = new UICollectionView(View.Frame, GetUiCollectionViewLayout())
{
BackgroundColor = UIColor.Brown,
DataSource = new MyViewUICollectionViewDataSource(),
ContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.Never,
TranslatesAutoresizingMaskIntoConstraints = false,
ShowsVerticalScrollIndicator = false,
ShowsHorizontalScrollIndicator = false
};
_collectionView.RegisterClassForCell(typeof(MyViewUICollectionViewCell), MyViewUICollectionViewCell.ReuseIdentifier);
_collectionView.RegisterClassForCell(typeof(MyViewUICollectionViewCell2), MyViewUICollectionViewCell2.ReuseIdentifier);
View.AddSubview(_collectionView);
NSLayoutConstraint.ActivateConstraints(new[]
{
_collectionView.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor),
_collectionView.BottomAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.BottomAnchor),
_collectionView.LeftAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.LeftAnchor),
_collectionView.RightAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.RightAnchor)
});
}
private static void PopulateModels()
{
for (var i = 0; i < 100; i++)
{
BaseModel baseModel;
if (i % 2 == 0)
{
baseModel = new Model1
{
UIColor = UIColor.Red
};
}
else
{
baseModel = new Model2
{
Text = "Item " + i
};
}
Models.Add(baseModel);
}
}
private UICollectionViewLayout GetUiCollectionViewLayout()
{
var layoutSize = NSCollectionLayoutSize.Create(NSCollectionLayoutDimension.CreateFractionalWidth(1), NSCollectionLayoutDimension.CreateEstimated(200));
var item = NSCollectionLayoutItem.Create(layoutSize);
var group = NSCollectionLayoutGroup.CreateHorizontal(layoutSize: layoutSize, subitem: item, count: 1);
var section = NSCollectionLayoutSection.Create(group);
// this is what you need for content inset
section.ContentInsets = new NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5);
// this is spacing between items
section.InterGroupSpacing = 5;
var layout = new UICollectionViewCompositionalLayout(section);
return layout;
}
}
public class BaseModel
{
}
public class Model1 : BaseModel
{
public UIColor UIColor { get; set; }
}
public class Model2 : BaseModel
{
public string Text { get; set; }
}
public class MyViewUICollectionViewDataSource : UICollectionViewDataSource
{
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var model = MyViewController.Models[(int) indexPath.Item];
switch (model)
{
case Model1 model1:
{
var cell = collectionView.DequeueReusableCell(MyViewUICollectionViewCell.ReuseIdentifier, indexPath) as MyViewUICollectionViewCell;
cell.ColorView.BackgroundColor = model1.UIColor;
return cell;
}
case Model2 model2:
{
var cell2 = collectionView.DequeueReusableCell(MyViewUICollectionViewCell2.ReuseIdentifier, indexPath) as MyViewUICollectionViewCell2;
cell2.Label.Text = model2.Text;
return cell2;
}
default:
throw new Exception();
}
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
return MyViewController.Models.Count;
}
}
public class MyViewUICollectionViewCell : UICollectionViewCell
{
public const string ReuseIdentifier = "MyViewUICollectionViewCell";
public UIView ColorView { get; }
[Export("initWithFrame:")]
public MyViewUICollectionViewCell(CGRect frame) : base(frame)
{
var container = new UIView
{
BackgroundColor = UIColor.White,
TranslatesAutoresizingMaskIntoConstraints = false
};
container.Layer.CornerRadius = 5;
ContentView.AddSubview(container);
container.TopAnchor.ConstraintEqualTo(ContentView.TopAnchor).Active = true;
container.LeftAnchor.ConstraintEqualTo(ContentView.LeftAnchor).Active = true;
container.BottomAnchor.ConstraintEqualTo(ContentView.BottomAnchor).Active = true;
container.RightAnchor.ConstraintEqualTo(ContentView.RightAnchor).Active = true;
ColorView = new UIView
{
TranslatesAutoresizingMaskIntoConstraints = false
};
ColorView.Layer.CornerRadius = 10;
container.AddSubview(ColorView);
ColorView.CenterXAnchor.ConstraintEqualTo(container.CenterXAnchor).Active = true;
ColorView.CenterYAnchor.ConstraintEqualTo(container.CenterYAnchor).Active = true;
ColorView.WidthAnchor.ConstraintEqualTo(20).Active = true;
ColorView.HeightAnchor.ConstraintEqualTo(50).Active = true;
}
}
public class MyViewUICollectionViewCell2 : UICollectionViewCell
{
public const string ReuseIdentifier = "MyViewUICollectionViewCell2";
public UILabel Label { get; }
[Export("initWithFrame:")]
public MyViewUICollectionViewCell2(CGRect frame) : base(frame)
{
var container = new UIView
{
BackgroundColor = UIColor.White,
TranslatesAutoresizingMaskIntoConstraints = false
};
container.Layer.CornerRadius = 5;
ContentView.AddSubview(container);
container.TopAnchor.ConstraintEqualTo(ContentView.TopAnchor).Active = true;
container.LeftAnchor.ConstraintEqualTo(ContentView.LeftAnchor).Active = true;
container.BottomAnchor.ConstraintEqualTo(ContentView.BottomAnchor).Active = true;
container.RightAnchor.ConstraintEqualTo(ContentView.RightAnchor).Active = true;
Label = new UILabel
{
TranslatesAutoresizingMaskIntoConstraints = false
};
container.AddSubview(Label);
Label.CenterXAnchor.ConstraintEqualTo(container.CenterXAnchor).Active = true;
Label.CenterYAnchor.ConstraintEqualTo(container.CenterYAnchor).Active = true;
//Label.WidthAnchor.ConstraintEqualTo(20).Active = true;
//Label.HeightAnchor.ConstraintEqualTo(20).Active = true;
}
}
如您所见,问题是两种细胞类型使用相同的估计高度 200。我不确定我做错了什么。我希望两种单元格类型都自动调整大小以适合其内容,而不是使用额外的 space 来达到 200。我尝试使用较小的估计大小,例如 10,但这并没有改变任何东西。应如何更改代码以具有有效的自动调整大小功能?
这取决于您在 CollectionViewCell
中设置的约束条件,请不要给标签指定固定大小,否则它不会自动调整大小,在 MyViewUICollectionViewCell2 中,将标签的约束条件更改为:
public class MyViewUICollectionViewCell2 : UICollectionViewCell
{
public const string ReuseIdentifier = "MyViewUICollectionViewCell2";
public UILabel Label { get; }
[Export("initWithFrame:")]
public MyViewUICollectionViewCell2(CGRect frame) : base(frame)
{
var container = new UIView
{
BackgroundColor = UIColor.White,
TranslatesAutoresizingMaskIntoConstraints = false
};
container.Layer.CornerRadius = 5;
ContentView.AddSubview(container);
container.TopAnchor.ConstraintEqualTo(ContentView.TopAnchor).Active = true;
container.LeftAnchor.ConstraintEqualTo(ContentView.LeftAnchor).Active = true;
container.BottomAnchor.ConstraintEqualTo(ContentView.BottomAnchor).Active = true;
container.RightAnchor.ConstraintEqualTo(ContentView.RightAnchor).Active = true;
Label = new UILabel
{
Lines = 0;
TranslatesAutoresizingMaskIntoConstraints = false
};
container.AddSubview(Label);
Label.TopAnchor.ConstraintEqualTo(container.TopAnchor).Active = true;
Label.LeftAnchor.ConstraintEqualTo(container.LeftAnchor).Active = true;
Label.BottomAnchor.ConstraintEqualTo(container.BottomAnchor).Active = true;
Label.RightAnchor.ConstraintEqualTo(container.RightAnchor).Active = true;
//Label.CenterXAnchor.ConstraintEqualTo(container.CenterXAnchor).Active = true;
//CenterYAnchor.ConstraintEqualTo(container.CenterYAnchor).Active = true;
//Label.WidthAnchor.ConstraintEqualTo(20).Active = true;
//Label.HeightAnchor.ConstraintEqualTo(20).Active = true;
}
}
对于MyViewUICollectionViewCell1
中的colorView
,如果您知道colorView
的大小,您应该将container
的大小指定为与[=相同的大小12=], 不会长成不同文字的label.