【问题标题】:How to create auto sized cells using UICollectionViewCompositionalLayout and multiple cell types?如何使用 UICollectionViewCompositionalLayout 和多种单元格类型创建自动调整大小的单元格?
【发布时间】:2020-02-19 06:31:11
【问题描述】:

我正在尝试在 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。我不确定我在这里做错了什么。我希望这两种单元格类型都能自动调整大小以适应它们的内容,而不是有额外的空间来达到 200。我尝试使用像 10 这样的小估计大小,但这并没有改变任何东西。应如何更改代码以具有工作的自动大小功能?

【问题讨论】:

    标签: c# swift xamarin.ios ios13 autosize


    【解决方案1】:

    这取决于您在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的大小与colorView的大小相同,它不会像标签一样增长不同的文字。

    【讨论】:

    • 您是否测试过您对 MyViewUICollectionViewCell2 更改的建议?我做了,似乎没有任何改变,所以你的建议对我不起作用。
    • @user246392 是的,我做了一个测试。稍后我将与您分享一个示例。
    • @user246392 我分享了示例here,您可以查看它并告诉我它是否适合您。
    • 我还找到了一个不错的博客here,您可以阅读以了解有关 uicollectionviewcompositionallayout 的更多信息。
    • 你能帮我修复一下stackoverflow.com/questions/58580490/…吗?我正在将我的代码从 Forms 迁移到 Xamarin.iOS,但我对很多 iOS 概念还是陌生。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-20
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    相关资源
    最近更新 更多