Xamarin表示iOS自定义UITableViewCell动态高度

时间:2018-01-04 10:33:32

标签: c# ios xamarin.forms

我试图为Xamarin Forms创建自定义UITableViewCell,它会根据其包含的文本自动调整它的高度。

我找到了通过Designer制作的解决方案,但我需要在代码中执行此操作。

这是我在分析Designer解决方案后现在所​​拥有的,但它不起作用:

public class FormLabelCellView : UITableViewCell, INativeElementView
{
    public UILabel Label { get; private set; }
    public FormLabelCell Cell { get; set; }
    public Element Element => Cell;

    public FormLabelCellView(FormLabelCell cell) : base(UITableViewCellStyle.Default, cell.GetType().FullName)
    {
        Cell = cell;
        // in the custom ContentView solution I can't set the cell to expand and contract according to the text it contains
        Label = new UILabel();

        Label.TranslatesAutoresizingMaskIntoConstraints = false;
        Label.SetContentCompressionResistancePriority(751, UILayoutConstraintAxis.Vertical);
        Label.SetContentHuggingPriority(251, UILayoutConstraintAxis.Vertical);

        this.ContentView.AddSubview(Label);

        this.ContentView.AddConstraint(NSLayoutConstraint.Create(Label, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ContentView, NSLayoutAttribute.Left, 1, 0));
        this.ContentView.AddConstraint(NSLayoutConstraint.Create(Label, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.ContentView, NSLayoutAttribute.Right, 1, 0));
        this.ContentView.AddConstraint(NSLayoutConstraint.Create(Label, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ContentView, NSLayoutAttribute.Top, 1, 0));
        this.ContentView.AddConstraint(NSLayoutConstraint.Create(Label, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.ContentView, NSLayoutAttribute.Bottom, 1, 0));
    }
}

这是结果(行高可能是DefaultRowHeight为44):

custom ContentView solution

在有人询问 RowHeight EstimatedRowHeight 之前。是的我在自定义ListViewRenderer中设置了 RowHeight = UITableView.AutomaticDimension; EstimatedRowHeight = 24;

我还可以在将自定义UITableViewCell修改为此时验证它是否有效:

public class FormLabelCellView : UITableViewCell, INativeElementView
{
    public UILabel Label { get; private set; }
    public FormLabelCell Cell { get; set; }
    public Element Element => Cell;

    public FormLabelCellView(FormLabelCell cell) : base(UITableViewCellStyle.Default, cell.GetType().FullName)
    {
        Cell = cell;
        // this solution works, but I can't remove the margins / padding and set background color
        Label = this.TextLabel;
    }
}

然后结果如下:

using TextLabel

我认为我没有正确创建约束。有人能帮助我吗?

编辑:尝试创建自定义UITableViewSource用于测试目的后,我发现问题出在 ListViewDataSource UnevenListViewDataSource 的Xamarin实现中。这很不幸,因为这些类是内部的,所以我不能扩展它们并覆盖GetHeightForRow函数。

3 个答案:

答案 0 :(得分:1)

在我的测试中,如果我在listViewRenderer中设置RowHeight = UITableView.AutomaticDimension;EstimatedRowHeight = 24;,我将获得与您显示的效果相同的效果:TableView似乎具有相等的行高。

但是当我尝试使用另一种方式时:在tableView的源代码中使用override方法,如:

public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
    return UITableView.AutomaticDimension;
}

public override nfloat EstimatedHeight(UITableView tableView, NSIndexPath indexPath)
{
    return 24;
}

工作正常(单元格将在运行时自动调整行高)。

此外,我真的建议您使用此方法构建您的Cell:

public MyListViewCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
     //add your controls in the cell's ContentView
}

然后我们可以在Source中使用它:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
    MyListViewCell cell = tableView.DequeueReusableCell("Cell") as MyListViewCell;

    if (cell == null)
    {
        cell = new MyListViewCell(new NSString("Cell"));
    }

    //Update your data

    return Cell;
}

答案 1 :(得分:0)

public void SetUpCell(string ParticipantName)
        {
            ParticipantNameLabel.Text = ParticipantName;
        }

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
        {
            var cell = tableView.DequeueReusableCell("PeopleCell", indexPath) as PeopleCell;

            if (cell == null)
            {
                cell = new PeopleCell(new NSString("PeopleCell"));
            }
            cell.SelectionStyle = UITableViewCellSelectionStyle.None;
            cell.SetUpCell(ListPeople[indexPath.Row]);

            return cell;
        }

当我调用cell.SetUpCell()时,我在ParticipantNameLabel上获得了一个空引用,我在上面演示的构造函数中实例化了它。

public MyListViewCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
     //add your controls in the cell's ContentView
}

如果我将实例化放在下面的构造函数中,我就不会得到这个空引用。

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

我错过了什么?

答案 2 :(得分:0)

所以回答我自己的问题。问题出在内部类 ListViewDataSource UnevenListViewDataSource 的Xamarin实现中。因为我们无法扩展这些类,所以ListView中有两种可能的动态行高度解决方案。

  1. 创建 UITableViewSource
  2. 的自己实现
  3. 使用 ViewCell 创建自定义单元格。
  4. 我使用了第二个选项,因为我正在进行多平台应用程序而且我不想创建 UITableViewSource 的复杂实现。

    所以这是自定义ViewCell的示例实现:

    public class CustomLabelCell : ViewCell
    {
        private Label label;
    
        public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(CustomLabelCell), null);
    
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
    
        // disable public set on View property
        public new View View
        {
            get { return base.View; }
            private set { base.View = value; }
        }
    
        public CustomLabelCell()
        {
            this.label = new Label()
            {
                Text = this.Text
            };
            ContentView frame = new ContentView()
            {
                Padding = new Thickness(4.0),
                Content = label
            };
            this.View = frame;
        }
    
        protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);
    
            if (propertyName == TextProperty.PropertyName)
            {
                this.label.Text = this.Text;
                ForceUpdateSize();
            }
        }
    }
    

    这就是你在ListView中使用它的方法:

    ListView listView = new ListView(ListViewCachingStrategy.RecycleElement)
    {
        HasUnevenRows = true,
        ItemsSource = new ObservableCollection<TestObj> { new TestObj("one - example text that will reach s e c o n d row"), new TestObj("two"), new TestObj("three - longer example text with more words for third row that will span across three rows"), new TestObj("four"), new TestObj("five") },
        ItemTemplate = new DataTemplate(typeof(CustomLabelCell))
    };
    listView.ItemTemplate.SetBinding(CustomLabelCell.TextProperty, "Name");
    

    为了完整性,这是TestObj的实现:

    public class TestObj : System.ComponentModel.INotifyPropertyChanged
    {
        private string name;
    
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs("Name"));
            }
        }
    
        public TestObj(string name)
        {
            this.name = name;
        }
    }
    

    如您所见,没有必要在平台特定代码中创建自定义渲染器和自定义单元格。