16
votes

I'm having an issue with resizing rows for Xamarin Forms when it comes to iOS. My code works perfectly on Android though. When I resize a row for iOS by increasing the size, then it will overlap the row below. Googling this gives me results like this: http://forums.xamarin.com/discussion/comment/67627/#Comment_67627

The results claim that Xamarin Forms has not implemented this due to performance problems. I only find results that are 1+ year old so I don't know if this still is the reason.

I'm finding a number of different ways to solve this in the different threads I've discovered but all are quite complicated.

For me it is very common in apps that an item is expanded upon selection. I was wondering if anyone here can suggest a solution that will solve this? If possible then I would really like to avoid recalculating exact heights over and over. Should I inject a custom list using Xamarin.iOS and dependency injection?

I'll post my XAML here but the XAML is probably not the problem since it is working just fine on Android.

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="simpletest.PlayGroundPage">
    <ContentPage.Content>
        <StackLayout>
            <ListView VerticalOptions="FillAndExpand" HasUnevenRows="True" SeparatorVisibility="None" ItemsSource="{Binding AllItems}" SelectedItem="{Binding MySelectedItem}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout VerticalOptions="FillAndExpand">
                                <Label Text="{Binding MyText}" />
                                <Button Text="button1" IsVisible="{Binding IsExtraControlsVisible}" />
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

My packages for the forms project:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="FreshEssentials" version="2.0.1" targetFramework="portable-net45+win+wp80+MonoTouch10+MonoAndroid10+xamarinmac20+xamarintvos10+xamarinwatchos10+xamarinios10" />
  <package id="Xamarin.Forms" version="2.3.1.114" targetFramework="portable-net45+win+wp80+MonoTouch10+MonoAndroid10+xamarinmac20+xamarintvos10+xamarinwatchos10+xamarinios10" />
</packages>

Here is my viewmodel.

public class PlayGroundViewModel : INotifyPropertyChanged
{

    private Item _mySelectedItem;
    private ObservableCollection<Item> _allItems;

    public PlayGroundViewModel(ObservableCollection<Item> allItems)
    {
        AllItems = allItems;
        MySelectedItem = allItems.First();

        ItemClicked = (obj) => { ExpandRow(obj); };

    }

    public ObservableCollection<Item> AllItems { get { return _allItems; } set { _allItems = value; SetChangedProperty("AllItems"); } }

    public Action<Item> ItemClicked { get; private set; }

    public Item MySelectedItem
    {
        get { return _mySelectedItem; }
        set { _mySelectedItem = value; SetChangedProperty("MySelectedItem"); }
    }

    private void ExpandRow(Item item)
    {
        foreach (Item x in AllItems)
        {
            x.IsExtraControlsVisible = false;
        }

        if (item != null)
        {
            item.IsExtraControlsVisible = true;
        }

        SetChangedProperty("MySelectedItem");
        SetChangedProperty("AllItems");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void SetChangedProperty(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

}

CodeBehind:

    public PlayGroundPage()
    {
        InitializeComponent();
        ObservableCollection<Item> items = new ObservableCollection<Item>();
        items.Add(new Item("One"));
        items.Add(new Item("Two"));
        items.Add(new Item("Three"));

        PlayGroundViewModel viewModel = new PlayGroundViewModel(items);
        BindingContext = viewModel;
        Action<Item> itemClickedAction = viewModel.ItemClicked;
        ItemList.ItemTapped += (object sender, ItemTappedEventArgs e) =>
          {
              Item item = (Item)e.Item;
              itemClickedAction.Invoke(item);
          };
    }
}

The Item:

public class Item : INotifyPropertyChanged
{


    public Item(string text)
    {
        _myText = text;
        _isExtraControlsVisible = false;
    }

    private string _myText;
    private bool _isExtraControlsVisible;

    public event PropertyChangedEventHandler PropertyChanged;


    public string MyText
    {
        get { return _myText; }
        set { _myText = value; OnPropertyChanged("MyText"); }
    }

    public bool IsExtraControlsVisible
    {
        get { return _isExtraControlsVisible; }
        set { _isExtraControlsVisible = value; OnPropertyChanged("IsExtraControlsVisible"); }
    }

    private void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}

I'll post two pictures below of a list view. The first picture is without clicking. The second picture is after having clicked on "Two". A button appears within the cell but the cell overlaps the cell below("Three").

Unclicked

Clicked

2
Depending on which version you're using, There is a ForceUpdateSize on the cell. The function does nothing on my version, but other people have said it works.Taekahn
I'd like to try that but I am suing XAML fully separated from my ViewModel and would like to avoid directly manipulating the GUI. Can this be triggered without referencing the actual list in C#?why_vincent

2 Answers

11
votes

I'm not sure why SushiHangover doesn't have the same issue, but i get the exact same results that you do. By default, mine don't resize either.

Regardless of if you want to use his suggestion on updating the item, or use the way you originally did it, this should force your cells to update to the correct size.

Change your ViewCell to handle a tap.

<ViewCell Tapped="Handle_Tapped">

then in the code-behind for your xaml page

void Handle_Tapped(object sender, System.EventArgs e)
{
    (sender as ViewCell).ForceUpdateSize();
}

The documentation for ForceUpdateSize() says to be careful with it, because it can be expensive, but if it's certainly going to be the least amount of effort to get it to do what you're looking for.

0
votes

Sadly for me, the ListView (nor the TableView) wasn't able to resize the height of its columns dynamically on iOS (on Android works perfectty) even calling the ForceUpdateSize() method. Currently I'm using Xamarin Forms 2.5.0.

Even more ridiculous, the cells do resize to the right height when I scroll down and then scroll back to visualize them again (!).

After a lot of research, and testing a lot of solutions, I decided to change the approach: I've created a StackLayout inside a ScrollView and added the ViewCells (that I had to change to ContentViews) dynamically in the code-behind to the StackLayout. I got this idea from https://stackoverflow.com/a/41157227/2816119

This make me think these Collection Views are not ready for this type of Use Cases.

You can find a lot of people suffering with this since 2014. Eg: https://xamarin.uservoice.com/forums/258559-xamarin-forms-suggestions/suggestions/6205412-make-it-possible-to-set-listview-cell-s-height-to#comments