当我上下滚动 UICollectionView 时,视图高度限制设置不正确

View height constraints are not set correctly as I scroll UICollectionView up and down

我正在尝试根据模型 属性 确定视图高度,但是随着 UICollectionView 上下滚动,为可见单元格指定的高度不正确。似乎在 GetCell 中设置 HeightAnchor(即 cellForItemAtIndexPath)不起作用。我怎样才能使这项工作?

using CoreGraphics;
using Foundation;
using System;
using System.Collections.Generic;
using UIKit;

namespace App2
{
    public partial class ViewController : UIViewController
    {
        private UICollectionView _collectionView;

        public ViewController (IntPtr handle) : base (handle)
        {
        }

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();

            InitializeCollectionView();
        }

        private void InitializeCollectionView()
        {
            _collectionView = new UICollectionView(View.Frame, new UICollectionViewCompositionalLayout(GetSection()))
            {
                DataSource = new CustomUICollectionViewDataSource(),
                TranslatesAutoresizingMaskIntoConstraints = false
            };

            _collectionView.RegisterClassForCell(typeof(CustomUICollectionViewCell), "CustomUICollectionViewCell");

            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 NSCollectionLayoutSection GetSection()
        {
            var size = NSCollectionLayoutSize.Create(NSCollectionLayoutDimension.CreateFractionalWidth(1), NSCollectionLayoutDimension.CreateEstimated(50));
            var item = NSCollectionLayoutItem.Create(size);
            var group = NSCollectionLayoutGroup.CreateHorizontal(layoutSize: size, subitem: item, count: 1);
            var section = NSCollectionLayoutSection.Create(group);

            section.InterGroupSpacing = 5;

            return section;
        }
    }

    public class CustomUICollectionViewDataSource : UICollectionViewDataSource
    {
        private readonly List<Model> _models = new List<Model>
        {
            new Model {Height = 250},
            new Model {Height = 100},
            new Model {Height = 300},
            new Model {Height = 400},
            new Model {Height = 500},
            new Model {Height = 50},
            new Model {Height = 230},
            new Model {Height = 100},
            new Model {Height = 600},
            new Model {Height = 310},
            new Model {Height = 150},
            new Model {Height = 220}
        };

        public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
        {
            var model = _models[(int)indexPath.Item];

            var cell = collectionView.DequeueReusableCell("CustomUICollectionViewCell", indexPath) as CustomUICollectionViewCell;

            cell.UpdateHeight(model.Height);

            return cell;
        }

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

    public sealed class CustomUICollectionViewCell : UICollectionViewCell
    {
        private readonly UIView _uiView;

        [Export("initWithFrame:")]
        public CustomUICollectionViewCell(CGRect frame) : base(frame)
        {
            _uiView = new UIView
            {
                BackgroundColor = UIColor.Brown,
                TranslatesAutoresizingMaskIntoConstraints = false
            };

            ContentView.AddSubview(_uiView);

            NSLayoutConstraint.ActivateConstraints(new[]
            {
                _uiView.TopAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.TopAnchor),
                _uiView.BottomAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.BottomAnchor),
                _uiView.LeftAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.LeftAnchor),
                _uiView.RightAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.RightAnchor)
            });
        }

        public void UpdateHeight(int height)
        {
            _uiView.HeightAnchor.ConstraintEqualTo(height).Active = true;
        }
    }

    public class Model
    {
        public int Height { get; set; }
    }
}

如果您这样做,打印消息会提示您重复约束。

您设置了left, right, bottom, top的约束,更新时添加了height约束。前四个约束已经确定了最终的高度,这里的新高度不起作用,会打印警告信息。

如果真的要更新高度,应该从头开始设置left, right, top, height约束,保存height约束,更新时用到

var heightConstraint: NSLayoutConstraint?

heightConstraint = _uiView.heightAnchor.constraint(equalToConstant: 50)//Defaults
NSLayoutConstraint.activate([

(_uiView.topAnchor.constraint(equalTo:ContentView.SafeAreaLayoutGuide.topAnchor))!,                
(_uiView.leftAnchor.constraint(equalTo:ContentView.SafeAreaLayoutGuide.leftAnchor))!
(_uiView.rightAnchor.constraint(equalTo:ContentView.SafeAreaLayoutGuide.rightAnchor))!,
(heightConstraint)!
]);

public void UpdateHeight(int height){
    heightConstraint?.isActive = false
    heightConstraint = _uiView.heightAnchor.constraint(equalToConstant: height)
    heightConstraint?.isActive = true
}

这是 Xamarin support 推荐的解决方法:

NSLayoutConstraint heightConstraint;
public void UpdateHeight(int height)
{
    if (heightConstraint == null)
    {
        heightConstraint = _uiView.HeightAnchor.ConstraintEqualTo(height);
        heightConstraint.Active = true;
    }
    else
    {
        heightConstraint.Constant = height;
    }
}