A temporary fix for me is to force the RowHeight of the given ListView or TableView.
e.g. ListView fred = new ListView { RowHeight = 100,
but ideally I would like the height of each row to depend on the content. For example, I might have a list of names where some of the names are particularly long (e.g. the classic Monty Python: 'Tarquin Fin-tim-lin-bin-whin-bim-lim-bus-stop-F'tang-F'tang-Olé-Biscuitbarrel', who was standing for election for the Silly Party, which is going to take two lines to display on most phones).
Trying to do the same thing, and it doesn't seem to make a difference whether HasUnevenRows is set to true or not.. The rows are all the same height. What's more is it would be great if Xamarin.Forms could expand the height of the cells to accomodate the content for me, rather than making me calculate the height... either way though, I can't get anything to work at the moment!
From the looks of things, HasUnevenRows is not implemented for the ListView, but it is for the TableView (I'm looking at IOS).. for the TableView there is a renderer UnEvenTableViewModelRenderer which overrides GetHeightForRow in the UITableViewSource... no such equivalent exists in the ListView from what I can see.
Unfortunately, it doesn't seem extensible enough to easily add this functionality myself.
I have opened a bug about this issue. And you are correct about List/TableView being hard to extend. They are far and away the hardest bits of code of any of the platforms, they make all the other controls look super-duper simple. In fact we had one guy pretty much dedicated to making them work xplat for about 3 months.
I hope as we are now at a point where things mostly work as expected (save for this bug here) we can look into some extensibility.
Well, given that I needed a listview with cells of different heights, I decided to break the rules and customize the IOS renderer. I share it here only as a proof of concept given the use of reflection to get at completely undocumented fields!
public class DynamicRowHeightListViewRenderer : ListViewRenderer
{
protected override void OnModelSet(VisualElement view)
{
base.OnModelSet(view);
var table = (UITableView)this.Control;
table.Source = new ListViewDataSourceWrapper(this.GetFieldValue<UITableViewSource>(typeof(ListViewRenderer), "dataSource"));
}
}
public class ListViewDataSourceWrapper : UITableViewSource
{
private readonly UITableViewSource underlyingTableSource;
public ListViewDataSourceWrapper(UITableViewSource underlyingTableSource)
{
this.underlyingTableSource = underlyingTableSource;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
return this.GetCellInternal(tableView, indexPath);
}
public override int RowsInSection(UITableView tableview, int section)
{
return this.underlyingTableSource.RowsInSection(tableview, section);
}
public override float GetHeightForHeader(UITableView tableView, int section)
{
return this.underlyingTableSource.GetHeightForHeader(tableView, section);
}
public override UIView GetViewForHeader(UITableView tableView, int section)
{
return this.underlyingTableSource.GetViewForHeader(tableView, section);
}
public override int NumberOfSections(UITableView tableView)
{
return this.underlyingTableSource.NumberOfSections(tableView);
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
this.underlyingTableSource.RowSelected(tableView, indexPath);
}
public override string[] SectionIndexTitles(UITableView tableView)
{
return this.underlyingTableSource.SectionIndexTitles(tableView);
}
public override string TitleForHeader(UITableView tableView, int section)
{
return this.underlyingTableSource.TitleForHeader(tableView, section);
}
public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
var uiCell = this.GetCellInternal(tableView, indexPath);
uiCell.SetNeedsLayout();
uiCell.LayoutIfNeeded();
var viewCell = uiCell.GetPropertyValue<ViewCell>(uiCell.GetType(), "ViewCell");
return (float)viewCell.RenderHeight;
}
private UITableViewCell GetCellInternal(UITableView tableView, NSIndexPath indexPath)
{
return this.underlyingTableSource.GetCell(tableView, indexPath);
}
}
public static class PrivateExtensions
{
public static T GetFieldValue<T>(this object @this, Type type, string name)
{
var field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField);
return (T)field.GetValue(@this);
}
public static T GetPropertyValue<T>(this object @this, Type type, string name)
{
var property = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty);
return (T)property.GetValue(@this);
}
}
@MartinBooth Thanks for ListView code for allowing editable fields!
Does anyone know how to make this work for a TableView as well? Simply setting it to be the type of TableView in the reflection call doesn't seem to work, as I am assuming the reason being the source is gotten a different way.
+1, any progress on this issue? as there is a bug report on 'HasUnevenRows not working/implemented on iOS' that currently got "Resolved Invalid" status https://bugzilla.xamarin.com/show_bug.cgi?id=22535
@DanielHalan.6266 HasUnevenRows partially works for me for TableView and for ListView. I mean, I can have list or table with different height rows, all I need is to calculate height first.
Calculating height could be tricky though and result code is not really nice, but it works and that's better than nothing
'HasUnevenRows' seems to work correctly for me on iOS. On Android, I have to set the Height of each cell individually. Setting 'RowHeight' on the parent ListView without setting the height within the cell causes bizarre behavior.
my question is similar to Alexey's. Is it a way to force cell resize dynamically after binding is done? basically I have a listview with custom viewcell which has grid with 2 rows. first row is always visible, the second one should be visible only when item is selected. so height of selected row is not equal to height of other rows. This behavior works perfectly on Android, but on iOS, it seems to ignore resizing of cell's content, so it messed with the content of next cell. my temporary workaround is to hide next cell, but I need a better solution...
@AntonM Do you have any solution for this I am having same problem. @ermau I am working on accordion list View for iOS. It all works fine when I specify ListView Height in XAML.
But I want cells to adjust according to the content without specifying height to cell.
@HrishiD I posted my workaround in the prev post, I know it's a little bit stupid, but I didn't find any other solution as it looks like cell high (on ios) is recalculated only when itemssource is updated...
@AntonM I too did the same thing. I created accordion list View with ViewCell and Set height of view Cell by calculating length of each element in a view cell.
I have one question how different screen sizes will be handled for Icons.
do I need to create png files for 4s 5s and 6s (i.e for iOS I am talking) ?
do I need to write code to detect screen size and then set Icons and images for that particular screen?
if any one could answer that will be great.
I've tried many solutions too, not working at all.
My content in my cell is updated when the RefreshControl is finished and then the cell height isn't updated untill I've scrolled that cell out of the screen shortly.
My content is very dynamic so can't set the height myself.
So i'm posting the question as well.. Any update on this?
So this is still a thing on iOS. I've got the same issue as @TimBrand - height of the cell doesn't change until I scroll it out of view and then back in (at which point the height "jumps" to the new size).
Come on Xamarin peeps. You can make async/awaits for google play services callbacks, but not get iOS to resize a list view item in-place, out-of-the-box?
Remind me why I'm spending nearly US$2000/year on droid & ios business licenses?
I have exactly same issue, I need to change row height after tap, my rows don't refresh until listview scroll (re-draw) Any help on this??? custom render?? any sample code??
thanks !
I have the same issue, a custom cell that I want to change the height of depending upon user input. As others have said, in iOS setting the cell height after it has been rendered does nothing until the user forces a refresh by scrolling or rotating the device.
Based on an earlier suggestion I am going to try to add another cell to the table view (Im not using binding for this) and then immediately remove it to see if it triggers an invalidate or refresh or something that will allow the new cell height to be used. That is a complete fudge though, I should be able to call an Invalidate or Refresh or something like that to force a recalc.
@JeroenBoumans.6481 your solution has actually worked, for me in my listview that was dynamically adding newer items into the list, during the apps execution. It has resized the listview, although at the moment all my images look magnified.
Thanks for your solution it works, but for the aesthetics of the UI that are messed up.
Answers
A temporary fix for me is to force the RowHeight of the given ListView or TableView.
e.g.
ListView fred = new ListView { RowHeight = 100,
but ideally I would like the height of each row to depend on the content. For example, I might have a list of names where some of the names are particularly long (e.g. the classic Monty Python: 'Tarquin Fin-tim-lin-bin-whin-bim-lim-bus-stop-F'tang-F'tang-Olé-Biscuitbarrel', who was standing for election for the Silly Party, which is going to take two lines to display on most phones).
You can set the property HasUnevenRows to true and then set the height of the cell.
Trying to do the same thing, and it doesn't seem to make a difference whether HasUnevenRows is set to true or not.. The rows are all the same height. What's more is it would be great if Xamarin.Forms could expand the height of the cells to accomodate the content for me, rather than making me calculate the height... either way though, I can't get anything to work at the moment!
From the looks of things, HasUnevenRows is not implemented for the ListView, but it is for the TableView (I'm looking at IOS).. for the TableView there is a renderer UnEvenTableViewModelRenderer which overrides GetHeightForRow in the UITableViewSource... no such equivalent exists in the ListView from what I can see.
Unfortunately, it doesn't seem extensible enough to easily add this functionality myself.
I have opened a bug about this issue. And you are correct about List/TableView being hard to extend. They are far and away the hardest bits of code of any of the platforms, they make all the other controls look super-duper simple. In fact we had one guy pretty much dedicated to making them work xplat for about 3 months.
I hope as we are now at a point where things mostly work as expected (save for this bug here) we can look into some extensibility.
Well, given that I needed a listview with cells of different heights, I decided to break the rules and customize the IOS renderer. I share it here only as a proof of concept given the use of reflection to get at completely undocumented fields!
Any news on this bug @JasonASmith or a confirmation on how to fix this? thx.
Am having the same issue. Are there any plans for Xamarin to update the renderer for iOS to handle this on a ListView?
Plus one for this!
HasUnevenRows was fixed for ListView on iOS in 1.2.1
I posted a super-simple example over here which links to the Evolve 13 sample Session list and cell.
@ermau - has HasUnevenRows been fixed in TableView as well? Doesn't seem to be working for me in 1.2.1.
thanks
@Al316 Can I see how you're using it? It was never (to my knowledge) broken for TableView.
@MartinBooth Thanks for ListView code for allowing editable fields!
Does anyone know how to make this work for a TableView as well? Simply setting it to be the type of TableView in the reflection call doesn't seem to work, as I am assuming the reason being the source is gotten a different way.
+1, any progress on this issue? as there is a bug report on 'HasUnevenRows not working/implemented on iOS' that currently got "Resolved Invalid" status
https://bugzilla.xamarin.com/show_bug.cgi?id=22535
@DanielHalan.6266
HasUnevenRows
partially works for me for TableView and for ListView. I mean, I can have list or table with different height rows, all I need is to calculate height first.Calculating height could be tricky though and result code is not really nice, but it works and that's better than nothing
Here's my gist for measuring text size on iOS:
https://gist.github.com/rudyryk/7b9633e847e727d12de7
Does anybody know how to "refresh" TableView after setting new height for already displayed cell?
It seems like setting new height is just ignored.
'HasUnevenRows' seems to work correctly for me on iOS. On Android, I have to set the Height of each cell individually. Setting 'RowHeight' on the parent ListView without setting the height within the cell causes bizarre behavior.
my question is similar to Alexey's. Is it a way to force cell resize dynamically after binding is done? basically I have a listview with custom viewcell which has grid with 2 rows. first row is always visible, the second one should be visible only when item is selected. so height of selected row is not equal to height of other rows. This behavior works perfectly on Android, but on iOS, it seems to ignore resizing of cell's content, so it messed with the content of next cell. my temporary workaround is to hide next cell, but I need a better solution...
very ugly workaround for my problem.
1) modify collection with some dummy item
page.cs:
2) manually set height of the cell
mycell.cs:
@AntonM Do you have any solution for this I am having same problem.
@ermau I am working on accordion list View for iOS. It all works fine when I specify ListView Height in XAML.
But I want cells to adjust according to the content without specifying height to cell.
@HrishiD I posted my workaround in the prev post, I know it's a little bit stupid, but I didn't find any other solution as it looks like cell high (on ios) is recalculated only when itemssource is updated...
@AntonM I too did the same thing. I created accordion list View with ViewCell and Set height of view Cell by calculating length of each element in a view cell.
I have one question how different screen sizes will be handled for Icons.
do I need to create png files for 4s 5s and 6s (i.e for iOS I am talking) ?
do I need to write code to detect screen size and then set Icons and images for that particular screen?
if any one could answer that will be great.
Almost a year later, this annoying bug is still not fixed!!!
Really annoying!
Any solution for dynamic row height. I cant able to see code.
Any solution for dynamic row height . I can't able to see code.
Any
Any
Any
Any solution for dynamic row height. I cant able to see code.
Have you tried setting the HasUnevenRows property to true?
I've tried many solutions too, not working at all.
My content in my cell is updated when the RefreshControl is finished and then the cell height isn't updated untill I've scrolled that cell out of the screen shortly.
My content is very dynamic so can't set the height myself.
So i'm posting the question as well.. Any update on this?
Any Solution of this problem??
So this is still a thing on iOS. I've got the same issue as @TimBrand - height of the cell doesn't change until I scroll it out of view and then back in (at which point the height "jumps" to the new size).
Come on Xamarin peeps. You can make async/awaits for google play services callbacks, but not get iOS to resize a list view item in-place, out-of-the-box?
Remind me why I'm spending nearly US$2000/year on droid & ios business licenses?
I have exactly same issue, I need to change row height after tap, my rows don't refresh until listview scroll (re-draw) Any help on this??? custom render?? any sample code??
thanks !
I have the same issue, a custom cell that I want to change the height of depending upon user input. As others have said, in iOS setting the cell height after it has been rendered does nothing until the user forces a refresh by scrolling or rotating the device.
Based on an earlier suggestion I am going to try to add another cell to the table view (Im not using binding for this) and then immediately remove it to see if it triggers an invalidate or refresh or something that will allow the new cell height to be used. That is a complete fudge though, I should be able to call an Invalidate or Refresh or something like that to force a recalc.
This worked for me:
HasUnevenRows = true, RowHeight = -1,
Are you sure? Because 'RowHeight = -1' didn't change anything for me.
@JeroenBoumans.6481 your solution has actually worked, for me in my listview that was dynamically adding newer items into the list, during the apps execution. It has resized the listview, although at the moment all my images look magnified.
Thanks for your solution it works, but for the aesthetics of the UI that are messed up.