Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically resize list row for iOS in Xamarin Forms

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

like image 260
why_vincent Avatar asked Aug 03 '16 11:08

why_vincent


1 Answers

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.

like image 114
Taekahn Avatar answered Oct 08 '22 15:10

Taekahn