Search code examples
iosxamarinuiviewviewrendering

Xamarin forms iOS UIView Renderer intermittent OnElementChanged in some cases


I am doing video matrix views (1x1-2x2-3x3 views) using FlowListView. For iOS app, UIView renderer is used to integrate with a third party video SDK. Here is the FlowListView.

    <flv:FlowListView x:Name="VideoFlowList" FlowColumnCount="{Binding ColumnCount}" RowHeight="{Binding RowHeight, Mode=TwoWay}"
                  SeparatorVisibility="None" HasUnevenRows="false" BackgroundColor="Transparent"
                  FlowColumnMinWidth="80" FlowTotalRecords="{Binding TotalRecords, Mode=TwoWay}" FlowItemsSource="{Binding Items}">

    <flv:FlowListView.FlowColumnTemplate>
        <DataTemplate>
            <Grid x:Name="VideoGrid" Padding="2" BackgroundColor="{Binding SelectedBorderColour, Mode=TwoWay}" RowSpacing="1" ColumnSpacing="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <video:CameraView x:Name="MyCameraView" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="Black" />
                <Image x:Name="AddIcon" Source="panel_add.png" Grid.Row="0" Grid.Column="0" IsVisible="{Binding CameraNotAssigned}"
                       HorizontalOptions="Center" VerticalOptions="Center" Aspect="AspectFit" BackgroundColor="Transparent" WidthRequest="50" HeightRequest="50">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding BindingContext.AddCameraCommand, Source={x:Reference BrowseItemsPage}}" 
                                              CommandParameter="{x:Reference VideoGrid}" NumberOfTapsRequired="1" />
                    </Image.GestureRecognizers>
                </Image>

                <Label x:Name="Label" HorizontalOptions="Fill" HorizontalTextAlignment="Center" VerticalOptions="End"
                       BackgroundColor="Silver" Opacity="0.5" Text="{Binding CameraName, Mode=TwoWay}" TextColor="Black"/>
            </Grid>
        </DataTemplate>
    </flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>

On the startup of the matrix views, it displays 4 views (ColumnCount = 2, ToTalRecords = 4) in the view model) and works everytime. Switching to any other views works too. But ocassionaly when switching to 4 views from a single view, there are only 2 new Elements instead of 4 in overrided OnElementChanged. How to ensure to get 4 new Elements everytime?

Please note that this issue doesn't happen when switching to 4 views from 9 views.

Here is the code of the ViewRenderer.

public class VideoRender : ViewRenderer<CameraView, UIVideoView>
{
    private UIVideoView _videoView;

    protected override void OnElementChanged(ElementChangedEventArgs<CameraView> e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null)
        {
            //Unsubscribe
            _videoView.Tapped -= OnVideoViewTapped;
            Log.Debug($"OldElement CameraIndex {e.OldElement.CameraIndex}, PlayWndHandle {e.OldElement.PlayWndHandle}");
        }
        if (e.NewElement != null)
        {
            //Control is TNativeView, CameraView
            if (Control == null)
            {
                _videoView = new UIVideoView(Element);
                SetNativeControl(_videoView);
            }

            //Element.PlayWndHandle = Handle;

            if (_videoView.Subviews.Length > 0)
            {
                Element.PlayWndHandle = _videoView.Subviews[0].Handle;
            }
            App.PlayWndHandles.Add(Element.PlayWndHandle);
            Log.Debug($"NewElement CameraIndex {Element.CameraIndex}, PlayWndHandle {Element.PlayWndHandle}");
            Log.Debug($"App.PlayWndHandles[{App.PlayWndHandles.Count - 1}]: {Element.PlayWndHandle}");

            // Subscribe
            _videoView.Tapped += OnVideoViewTapped;
        }
    }

}

I added a subview in the UIView UIVideoView and play the video in the subview. Is the issue related to the way I use the subview?
        
void Initialize()
{
    //place the video in subview
    var subView = new UIView();
    subView.UserInteractionEnabled = true;
    AddSubview(subView);
    if (Subviews.Length > 0)
    {
        Subviews[0].Frame = new CoreGraphics.CGRect(0, 0, 100, 100);
    }
}

Solution

  • It appears that using 4 separate lists of play window handles (one for each matrix size) can overcome the handle management issue. A single view is specially handled by clearing the handles in the list because the handle will always be removed and re-created.