如何在 iOS 上使用自定义渲染器隐藏 Xamarin.Forms TableView 页眉/页脚?

苦参

我想创建一个页面布局,在其中显示多个TableViews 以及其他视图,例如Label每个视图之间的a

为此,我需要具备以下能力:

  1. 使TableView不默认为垂直扩展布局选项(默认看起来不可更改),以便它们可以StackLayout垂直堆叠
  2. 隐藏相应的UITableView部分页眉/页脚,以便它们之间没有过多的垂直间距。

我通过以下CustomTableView控制实现了这一点

public class CustomTableView : TableView
{
    public bool NoHeader { get; set; }
    public bool NoFooter { get; set; }

    protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
    {
        if(!VerticalOptions.Expands)
        {
            // Call OnSizeRequest that is overwritten in custom renderer
            var baseOnSizeRequest = GetVisualElementOnSizeRequest();
            return baseOnSizeRequest(widthConstraint, heightConstraint);
        }

        return base.OnSizeRequest(widthConstraint, heightConstraint);
    }

    public Func<double, double, SizeRequest> GetVisualElementOnSizeRequest()
    {
        var handle = typeof(VisualElement).GetMethod(
            "OnSizeRequest",
            BindingFlags.Instance | BindingFlags.NonPublic,
            null,
            new Type[] { typeof(double), typeof(double) },
            null)?.MethodHandle;

        var pointer = handle.Value.GetFunctionPointer();
        return (Func<double, double, SizeRequest>)Activator.CreateInstance(
            typeof(Func<double, double, SizeRequest>), this, pointer);
    }
}

以及用于此的自定义 iOS 渲染器,它遵循此 SO 答案中隐藏UITableView页眉/页脚的建议:

public class CustomTableViewRenderer : TableViewRenderer
{
    public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
    {
        Control.LayoutIfNeeded();
        var size = new Size(Control.ContentSize.Width, Control.ContentSize.Height);
        return new SizeRequest(size);
    }

    protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
    {
        base.OnElementChanged(e);

        if(e.NewElement is CustomTableView view)
        {
            Control.ScrollEnabled = false;
            SetSource();
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        var view = (CustomTableView)Element;
        if(e.PropertyName == TableView.HasUnevenRowsProperty.PropertyName)
        {
            SetSource();
        }
    }

    private void SetSource()
    {
        var view = (CustomTableView)Element;
        if(view.NoFooter || view.NoHeader)
        {
            Control.Source = new CustomTableViewModelRenderer(view);
        }
        else
        {
            Control.Source = Element.HasUnevenRows ? new UnEvenTableViewModelRenderer(Element) :
                new TableViewModelRenderer(Element);
        }
    }

    public class CustomTableViewModelRenderer : UnEvenTableViewModelRenderer
    {
        private readonly CustomTableView _view;

        public CustomTableViewModelRenderer(CustomTableView model)
            : base(model)
        {
            _view = model;
        }

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

            return base.GetHeightForRow(tableView, indexPath);
        }

        public override nfloat GetHeightForHeader(UITableView tableView, nint section)
        {
            if(_view.NoHeader)
            {
                return 0.00001f;
            }

            return base.GetHeightForHeader(tableView, section);
        }

        public override UIView GetViewForHeader(UITableView tableView, nint section)
        {
            if(_view.NoHeader)
            {
                return new UIView(new CGRect(0, 0, 0, 0));
            }

            return base.GetViewForHeader(tableView, section);
        }

        public override nfloat GetHeightForFooter(UITableView tableView, nint section)
        {
            if(_view.NoFooter)
            {
                return 0.00001f;
            }

            return 10f;
        }

        public override UIView GetViewForFooter(UITableView tableView, nint section)
        {
            if(_view.NoFooter)
            {
                return new UIView(new CGRect(0, 0, 0, 0));
            }

            return base.GetViewForFooter(tableView, section);
        }
    }
}

然后我创建了一个简单的Xamarin.Forms ContentPage来实现这个:

public class TablePage : ContentPage
{
    public TablePage()
    {
        Table1 = new PageTableView
        {
            BackgroundColor = Color.Cyan,
            Root = new TableRoot
            {
                new TableSection("Table 1")
                {
                    new LabelEntryTableCell("Label A"),
                    new LabelEntryTableCell("Label B")
                }
            }
        };

        var label1 = new Label
        {
            Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
                "Vivamus accumsan lacus orci. Nulla in enim erat.",
            Margin = new Thickness(15, 5, 15, 0),
            BackgroundColor = Color.Yellow
        };

        Table2 = new PageTableView
        {
            BackgroundColor = Color.Red,
            Root = new TableRoot
            {
                new TableSection("Table 2")
                {
                    new LabelEntryTableCell("Label C"),
                    new LabelEntryTableCell("Label D")
                }
            }
        };

        var label2 = new Label
        {
            Text = "Duis vulputate mattis elit. " +
                "Donec vulputate lorem vitae elit posuere, quis consequat ligula imperdiet.",
            Margin = new Thickness(15, 5, 15, 0),
            BackgroundColor = Color.Green
        };

        StackLayout = new StackLayout
        {
            Children = { Table1, label1, Table2, label2 },
            Spacing = 0
        };

        BackgroundColor = Color.Gray;
        Title = "Custom Table View";
        Content = new ScrollView
        {
            Content = StackLayout
        };
    }

    public PageTableView Table1 { get; set; }
    public PageTableView Table2 { get; set; }
    public StackLayout StackLayout { get; set; }
}

public class PageTableView : CustomTableView
{
    public PageTableView()
    {
        Intent = TableIntent.Settings;
        HasUnevenRows = true;
        RowHeight = -1;
        VerticalOptions = LayoutOptions.Start;
        NoFooter = true;
    }
}

public class LabelEntryTableCell : ViewCell
{
    public LabelEntryTableCell(string labelText)
    {
        var label = new Label { Text = labelText };
        var entry = new Entry();

        View = new StackLayout
        {
            Children = { label, entry },
            BackgroundColor = Color.White,
            Padding = 10
        };
    }
}

这一切似乎都按预期工作,但是,当第TablePage一个显示CustomTableView内容被切断时。然后我可以将设备旋转到横向,问题会自行纠正,按照我的意图显示我的布局。然后将设备旋转回纵向仍然显示正确的布局,CustomTableView并且在该方向上不再有更多信息被切断。这种情况每次都持续发生。在此处查看此演示:

旋转解决问题

我还可以通过将其添加到以下内容来解决问题TablePage

protected override void OnAppearing()
{
    base.OnAppearing();

    // This will also fix the problem, but you see a flash of the old layout
    // when the page first displays.

    StackLayout.RedrawLayout();
}

...

public class CustomStackLayout : StackLayout
{
    public void RedrawLayout()
    {
        InvalidateLayout();
    }
}

How do I correct this problem? My custom control/renderer/page works as intended whenever I do the rotation/OnAppearing hack to correct it and I am not sure why. It seems like I need to call some re-draw method on the UITableView.

Sample Project

If you want to see the full source/project for this isolated test scenario you can find it on GitHub here.

Anonymous

I knew this issue in iOS. When the tableView first loaded, we can't get the correct contentSize even if we have written LayoutIfNeeded(). But after the page is appearing then we refresh it, we can get the correct contentSize. This is why

This will also fix the problem, but you see a flash of the old layout

解决方案 1:我的建议是我们可以尝试在 tableView 显示时延迟一点毫秒刷新 tableView:

在 TablePage 中,我订阅了一个 MessagingCenter 来刷新布局

MessagingCenter.Subscribe<object>(this, "Refresh", (sender) =>
{
    StackLayout.RedrawLayout();
});

然后我在渲染器中发送消息:

//I define this bool field to make sure this message only be sent at the first time after tableView loaded
private bool shouldRefresh = true;

public override async void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{

    if (tableView.NumberOfRowsInSection(0) - 1 == indexPath.Row)
    {
        if (shouldRefresh)
        {
            await Task.Delay(10);
            MessagingCenter.Send<object>(this, "Refresh");
            shouldRefresh = false;
        }  
    }
}

Solutin2:我们也可以尝试定义一个估计的rowHeight,在tableView第一次加载时给出一个contentSize:

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

但是在这个解决方案中,高度必须由我们自己预定义和计算。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何使用Xamarin Forms自定义渲染器隐藏Android选项卡?

来自分类Dev

如何在 Xamarin Forms 的不同版本的 Android 上使用不同的自定义渲染器?

来自分类Dev

如何在自定义渲染器中渲染xamarin.forms视图

来自分类Dev

将样式应用于Xamarin.Forms中的iOS自定义渲染器

来自分类Dev

Xamarin Forms - iOS 自定义渲染器 - 处理观察者

来自分类Dev

Xamarin Forms - 在 ios 中居中页面标题的自定义渲染器

来自分类Dev

如何使用自定义渲染器更改TableSection文本颜色-Xamarin.Forms C#

来自分类Dev

Xamarin IOS在自定义渲染器中基于IsFocused属性隐藏搜索栏图标

来自分类Dev

Xamarin.Forms上的自定义渲染器可以找到OnElementChanged方法

来自分类Dev

如何在Xamarin表单的自定义页面渲染器上实现mapbox?

来自分类Dev

使用自定义渲染器强制重绘Xamarin.Forms视图

来自分类Dev

使用自定义渲染器的Xamarin.Forms中的CollectionView

来自分类Dev

如何创建tableview部分之间的间距?(每个部分的页眉和页脚已经是自定义视图)

来自分类Dev

如何在没有自定义渲染器的情况下更改Xamarin Forms Cross Platform中Listview所选项目的背景颜色

来自分类Dev

如何在Xamarin共享项目中使用自定义渲染器

来自分类Dev

如何在Xamarin Forms for iOS的ScrollView上禁用弹跳功能?

来自分类Dev

如何使用Js和iOS-14在WKWebView中隐藏页眉和页脚

来自分类Dev

Xamarin.Forms:用于在UITableViewCell上显示公开指示器的自定义渲染器

来自分类Dev

Xamarin.Forms:用于在UITableViewCell上显示公开指示器的自定义渲染器

来自分类Dev

如何在Xamarin.Forms中访问TableView的子级

来自分类Dev

Xamarin.Forms自定义操作栏在iOS上

来自分类Dev

如何使用Angularjs包含页眉和页脚?

来自分类Dev

Xamarin Forms Maps自定义Android渲染器GetMapAsync不调用OnMapReady

来自分类Dev

Mac和UWP中的LibVLCSharp VideoView的自定义渲染器(Xamarin.Forms)

来自分类Dev

Xamarin.Forms.Maps 自定义折线渲染器在视图中不呈现

来自分类Dev

在Xamarin iOS中单击tableview委托时如何隐藏视图?

来自分类Dev

Xamarin.Forms:如何在 iOS 设备上隐藏导航栏分隔符?

来自分类Dev

iOS Swift Web视图控件,隐藏页眉和页脚

来自分类Dev

ZXing Xamarin形成iOS白色背景(无自定义渲染器)

Related 相关文章

  1. 1

    如何使用Xamarin Forms自定义渲染器隐藏Android选项卡?

  2. 2

    如何在 Xamarin Forms 的不同版本的 Android 上使用不同的自定义渲染器?

  3. 3

    如何在自定义渲染器中渲染xamarin.forms视图

  4. 4

    将样式应用于Xamarin.Forms中的iOS自定义渲染器

  5. 5

    Xamarin Forms - iOS 自定义渲染器 - 处理观察者

  6. 6

    Xamarin Forms - 在 ios 中居中页面标题的自定义渲染器

  7. 7

    如何使用自定义渲染器更改TableSection文本颜色-Xamarin.Forms C#

  8. 8

    Xamarin IOS在自定义渲染器中基于IsFocused属性隐藏搜索栏图标

  9. 9

    Xamarin.Forms上的自定义渲染器可以找到OnElementChanged方法

  10. 10

    如何在Xamarin表单的自定义页面渲染器上实现mapbox?

  11. 11

    使用自定义渲染器强制重绘Xamarin.Forms视图

  12. 12

    使用自定义渲染器的Xamarin.Forms中的CollectionView

  13. 13

    如何创建tableview部分之间的间距?(每个部分的页眉和页脚已经是自定义视图)

  14. 14

    如何在没有自定义渲染器的情况下更改Xamarin Forms Cross Platform中Listview所选项目的背景颜色

  15. 15

    如何在Xamarin共享项目中使用自定义渲染器

  16. 16

    如何在Xamarin Forms for iOS的ScrollView上禁用弹跳功能?

  17. 17

    如何使用Js和iOS-14在WKWebView中隐藏页眉和页脚

  18. 18

    Xamarin.Forms:用于在UITableViewCell上显示公开指示器的自定义渲染器

  19. 19

    Xamarin.Forms:用于在UITableViewCell上显示公开指示器的自定义渲染器

  20. 20

    如何在Xamarin.Forms中访问TableView的子级

  21. 21

    Xamarin.Forms自定义操作栏在iOS上

  22. 22

    如何使用Angularjs包含页眉和页脚?

  23. 23

    Xamarin Forms Maps自定义Android渲染器GetMapAsync不调用OnMapReady

  24. 24

    Mac和UWP中的LibVLCSharp VideoView的自定义渲染器(Xamarin.Forms)

  25. 25

    Xamarin.Forms.Maps 自定义折线渲染器在视图中不呈现

  26. 26

    在Xamarin iOS中单击tableview委托时如何隐藏视图?

  27. 27

    Xamarin.Forms:如何在 iOS 设备上隐藏导航栏分隔符?

  28. 28

    iOS Swift Web视图控件,隐藏页眉和页脚

  29. 29

    ZXing Xamarin形成iOS白色背景(无自定义渲染器)

热门标签

归档