You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI ListView在iOS中实现可展开行的方法

.NET MAUI iOS ListView 自适应行高优化方案

核心问题根源

iOS原生UITableView处理动态行高时,默认会缓存行高,单元格内容变化后不会主动重新计算行高。MAUI中HasUnevenRows对iOS的适配存在局限性,直接调用ForceUpdateSize()会触发整个ListView重绘,导致性能下降。

优化方案详细落地(对应Stack Overflow方案的深入实现)

1. 自定义ListView控件

创建带行高更新命令的自定义ListView,用来通知渲染器精准更新指定行高度:

public class AutoSizeListView : ListView
{
    public static readonly BindableProperty UpdateRowHeightCommandProperty =
        BindableProperty.Create(nameof(UpdateRowHeightCommand), typeof(ICommand), typeof(AutoSizeListView));

    public ICommand UpdateRowHeightCommand
    {
        get => (ICommand)GetValue(UpdateRowHeightCommandProperty);
        set => SetValue(UpdateRowHeightCommandProperty, value);
    }

    public void RequestRowHeightUpdate(int rowIndex)
    {
        UpdateRowHeightCommand?.Execute(rowIndex);
    }
}

2. 实现iOS自定义渲染器

在iOS项目中编写渲染器,利用原生UITableView的API仅刷新指定行,避免全局重绘:

[assembly: ExportRenderer(typeof(AutoSizeListView), typeof(AutoSizeListViewRenderer))]
namespace YourAppName.iOS.Renderers
{
    public class AutoSizeListViewRenderer : ListViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
        {
            base.OnElementChanged(e);

            if (Control != null && e.NewElement is AutoSizeListView autoSizeListView)
            {
                // 开启原生自动行高计算
                Control.RowHeight = UITableView.AutomaticDimension;
                // 设置合理预估行高,减少布局计算开销
                Control.EstimatedRowHeight = 100;

                // 绑定行高更新命令,仅刷新指定行
                autoSizeListView.UpdateRowHeightCommand = new Command<int>(index =>
                {
                    if (Control?.DataSource != null)
                    {
                        var indexPath = NSIndexPath.FromRowSection(index, 0);
                        Control.ReloadRows(new[] { indexPath }, UITableViewRowAnimation.Automatic);
                    }
                });
            }
        }
    }
}

3. ViewModel与XAML布局适配

  • ViewModel层:给问答项添加展开状态属性,绑定行索引和更新命令:
public class QaItemViewModel : INotifyPropertyChanged
{
    private bool _isExpanded;
    public bool IsExpanded
    {
        get => _isExpanded;
        set
        {
            _isExpanded = value;
            OnPropertyChanged();
            ExpandCommand?.Execute(this);
        }
    }

    public string Question { get; set; }
    public string Answer { get; set; }
    public int RowIndex { get; set; }
    public ICommand ExpandCommand { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<QaItemViewModel> QaItems { get; set; }
    public ICommand UpdateRowHeightCommand { get; set; }

    public MainViewModel()
    {
        QaItems = new ObservableCollection<QaItemViewModel>();
        // 初始化示例数据,为每个项分配行索引
        for (int i = 0; i < 20; i++)
        {
            QaItems.Add(new QaItemViewModel
            {
                Question = $"问题{i+1}",
                Answer = $"这是问题{i+1}的详细答案,内容较长,展开后需要显示全部内容。重复内容模拟长文本:这是问题{i+1}的详细答案,内容较长,展开后需要显示全部内容。",
                RowIndex = i,
                ExpandCommand = new Command<QaItemViewModel>(item =>
                {
                    UpdateRowHeightCommand?.Execute(item.RowIndex);
                })
            });
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  • XAML布局:使用自定义ListView,绑定数据并控制答案显示状态:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourAppName"
             x:Class="YourAppName.MainPage">
    <ContentPage.Resources>
        <local:BoolToTextConverter x:Key="BoolToTextConverter"/>
    </ContentPage.Resources>
    <ContentPage.BindingContext>
        <local:MainViewModel />
    </ContentPage.BindingContext>

    <local:AutoSizeListView ItemsSource="{Binding QaItems}"
                            HasUnevenRows="True"
                            UpdateRowHeightCommand="{Binding UpdateRowHeightCommand}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <VerticalStackLayout Padding="12,8">
                        <Label Text="{Binding Question}" FontSize="16" FontAttributes="Bold"/>
                        <Button Margin="0,8,0,0"
                                Text="{Binding IsExpanded, Converter={StaticResource BoolToTextConverter}, ConverterParameter='收起|展开'}" 
                                Command="{Binding ExpandCommand}" CommandParameter="{Binding .}"/>
                        <Label Text="{Binding Answer}" FontSize="14" IsVisible="{Binding IsExpanded}" LineBreakMode="WordWrap"/>
                    </VerticalStackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </local:AutoSizeListView>
</ContentPage>

4. 补充:布尔值转文本转换器

实现转换器让按钮文本随展开状态切换:

public class BoolToTextConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool isExpanded && parameter is string param)
        {
            var parts = param.Split('|');
            return isExpanded ? parts[0] : parts[1];
        }
        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

方案优势

  • 仅刷新指定行的高度,避免ForceUpdateSize()导致的全局重绘,大幅提升iOS端性能
  • 贴合iOS原生UITableView的布局逻辑,适配更稳定
  • 保留MAUI MVVM架构,无需修改原有业务逻辑

内容的提问来源于stack exchange,提问作者Shaurya Shikhar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 18:45:32