Maui
Maui copied to clipboard
[BUG] Expander is not working in ios inside collectionview
Is there an existing issue for this?
- [X] I have searched the existing issues
Did you read the "Reporting a bug" section on Contributing file?
- [X] I have read the "Reporting a bug" section on Contributing file: https://github.com/CommunityToolkit/Maui/blob/main/CONTRIBUTING.md#reporting-a-bug
Current Behavior
The expander control in iOS is not functioning as expected when placed inside a collection view. Specifically, only the first item of the collection view is affected when you click on any other item. This issue started occurring after upgrading the toolkit from 5.2.0 to 7.0.1 and .NET from 7.0 to 8.0.
Expected Behavior
When expander is placed inside collectionview, the item which is tapped or clicked must expand or collapsed. In android, it is working fine but has issue on iOS.
Steps To Reproduce
Create a collection view add expanded control inside it Run the application in ios
Link to public reproduction project repository
https://github.com/dotnet/maui/issues/20004
Environment
- .NET MAUI CommunityToolkit: 7.0.1
- OS: iOS 14.2.1 (23C71)
- .NET MAUI:.net 8
Anything else?
na
Hi @NetCSDev. We have added the "needs reproduction" label to this issue, which indicates that we cannot take further action. This issue will be closed automatically in 5 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
I tried to reproduce this bug in MauiCommunityToolkit sample app and yes expander is not working as it supposed to work in IOS. android-expander.webm
https://github.com/CommunityToolkit/Maui/assets/98330987/7e257255-c2d9-47ee-a386-6248d0f61af9
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 3 days. It will be closed if no further activity occurs within 2 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.
@TRybina132 Thanks for reproducing the bug.
+1. Having to resort to using a ListView with a custom ResizingViewCell.
@bradencohen would you be able to post the code for your ListView w/ custom ResizingViewCell for us to use in the interrim?
Certainly!
It really isn't pretty, & may be over-done for my specific use-case, so feel free to take what you need.
First, a custom handler is required for iOS ListView:
internal class ListViewHandler : Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer
{
public event Action ViewCellSizeChangedEvent
{
add { _viewCellSizeChangedEvent += value; }
remove { _viewCellSizeChangedEvent -= value; }
}
private Action _viewCellSizeChangedEvent;
public ListViewHandler()
{
ViewCellSizeChangedEvent += ListViewHandler_ViewCellSizeChangedEvent;
}
private void ListViewHandler_ViewCellSizeChangedEvent()
{
var tv = Control as UITableView;
if ( tv == null ) return;
tv.BeginUpdates();
tv.EndUpdates();
}
public void RaiseViewCellSizeChangedEvent()
{
_viewCellSizeChangedEvent?.Invoke();
}
}
Make sure to register in MauiProgram.
Second, I made an ExpanderViewCell:
public class ExpanderViewCell : Microsoft.Maui.Controls.ViewCell
{
public static event Action ViewCellSizeChangedEvent;
public static readonly BindableProperty ExpandedViewProperty =
BindableProperty.Create( nameof( ExpandedView ), typeof( View ), typeof( ExpanderViewCell ) );
public static readonly BindableProperty HeaderViewProperty =
BindableProperty.Create( nameof( HeaderView ), typeof( View ), typeof( ExpanderViewCell ) );
public static readonly BindableProperty IsExpandedProperty =
BindableProperty.Create( nameof( IsExpanded ), typeof( bool ), typeof( ExpanderViewCell ), false );
public static readonly BindableProperty DividerViewProperty =
BindableProperty.Create( nameof( DividerView ), typeof( View ), typeof( ExpanderViewCell ) );
public View ExpandedView
{
get { return ( View ) GetValue( ExpandedViewProperty ); }
set { SetValue( ExpandedViewProperty, value ); }
}
public View HeaderView
{
get { return ( View ) GetValue( HeaderViewProperty ); }
set { SetValue( HeaderViewProperty, value ); }
}
public View DividerView
{
get { return ( View ) GetValue( DividerViewProperty ); }
set { SetValue( DividerViewProperty, value ); }
}
public bool IsExpanded
{
get { return ( bool ) GetValue( IsExpandedProperty ); }
set { SetValue( IsExpandedProperty, value ); }
}
public static readonly BindableProperty UpdateExpandedOnTappedProperty = BindableProperty.CreateAttached(
propertyName: "UpdateExpandedOnTapped",
returnType: typeof( bool ),
declaringType: typeof( ExpanderViewCell ),
defaultValue: false,
propertyChanged: OnUpdateExpandedOnTappedChanged );
public static void OnUpdateExpandedOnTappedChanged( BindableObject bindable, object oldValue, object newValue )
{
// Once the view is attached to the visual tree, we can add the gesture recognizer
if ( ( bindable is not View view ) )
{
return;
}
view.Effects.Add( new OnAttachedListenerEffect
{
OnAttachedToWindow = () =>
{
var closestExpanderViewCell = view.ClosestAncestor<ExpanderViewCell>();
if ( closestExpanderViewCell is not null )
{
if ( newValue is bool updateExpandedOnTapped )
{
if ( updateExpandedOnTapped )
{
view.GestureRecognizers.Add( new TapGestureRecognizer
{
Command = new Command( () => closestExpanderViewCell.IsExpanded = !closestExpanderViewCell.IsExpanded )
} );
}
}
}
}
}
);
}
public static void SetUpdateExpandedOnTapped( BindableObject bindable, bool value ) => bindable.SetValue( UpdateExpandedOnTappedProperty, value );
public static bool GetUpdateExpandedOnTapped( BindableObject bindable ) => ( bool ) bindable.GetValue( UpdateExpandedOnTappedProperty );
private void UpdateExpandedOnTapped( object sender, EventArgs e )
{
IsExpanded = !IsExpanded;
}
protected override void OnParentSet()
{
base.OnParentSet();
if ( Parent != null )
{
ConfigureContent();
}
}
protected override void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
base.OnPropertyChanged( propertyName );
if ( propertyName == nameof( IsExpanded ) )
{
OnExpandedChanged();
}
}
private void ConfigureContent()
{
var expanderGrid = new Grid
{
RowDefinitions =
{
new RowDefinition( GridLength.Auto ),
new RowDefinition( GridLength.Auto ),
new RowDefinition( GridLength.Auto ),
}
};
if ( HeaderView is not null )
{
expanderGrid.Add( HeaderView, 0, 0 );
HeaderView.GestureRecognizers.Add( new TapGestureRecognizer
{
Command = new Command( () => IsExpanded = !IsExpanded )
} );
}
if ( ExpandedView is not null )
{
expanderGrid.Add( ExpandedView, 0, 1 );
ExpandedView.SetBinding( View.IsVisibleProperty, new Binding( nameof( IsExpanded ), source: this ) );
}
if ( DividerView is not null )
{
expanderGrid.Add( DividerView, 0, 2 );
}
View = expanderGrid;
}
private void OnExpandedChanged()
{
if ( Parent is not ListView listView )
{
return;
}
#if IOS
if( listView.Handler is Handlers.ListViewHandler listViewHandler )
{
listViewHandler.RaiseViewCellSizeChangedEvent();
}
#endif
}
}
You probably won't need all of this class. I added an attached property to the view cell (UpdateExpandedOnTapped) in cases where I need to specify the exact view that triggers expanding/not. That property an OnAttachedListener effect that we've found useful to trigger an event when a view gets attached to the window.
Then usage is like:
<DataTemplate x:Key="PersonSearchResultItemNoDivider">
<local:ExpanderViewCell>
<local:ExpanderViewCell.ExpandedView>
</local:ExpanderViewCell.ExpandedView>
<local:ExpanderViewCell.HeaderView>
</local:ExpanderViewCell.HeaderView>
</Rock:ExpanderViewCell>
</DataTemplate>
Then I just set HasUnevenRows to true on the ListView.
related https://github.com/dotnet/maui/issues/21141
@bradencohen do you mind showing the code for OnAttachedListener?
Is it just me that is finding it astonishing that we are repeatedly getting issues like this? FIVE MONTHS after being reported, its not fixed? Are we back to 'not fixing issues in 8 , we're developing .net 9 now' ? It feels like all I'm doing while trying to develop, is get updates to fix bugs, then later find new bugs in the new releases which I now have to find different solutions for, instead of working on the actual app. I guess it's my fault for not understanding enough so that I can just take bradens solution.... I'll try another layout...
@DeFeBe given that we all do this for free and in our spare time I don't believe there shouldn't be any expectation on when things get fixed.
This is a community toolkit which means it's built by the community, for the community. We openly accept PRs. If something is urgent then feel free to submit a fix.
It may be a "community toolkit", but microsoft direct people to it for functionality. Maui is being pushed as 'make your projects in it', but it relies on people giving up their free time. This just seems utterly astonishing and is of course aimed at microsoft management. Do we bin Maui and start again in something else or spend thousands on other companies sdk's (which aren't exactly bug free) ? As for a PR, this is in a PR, its a 6 months open bug which I found whilst trying to address non functionality in the ios version. It would be better if it was removed rather than be broken.
Still having this Issue, any updates?
We’ll see if new CollectionView handler fixes the issue.