DLToolkit.Forms.Controls
DLToolkit.Forms.Controls copied to clipboard
Extra new elements from OnElementChanged in UIView on iOS
I am doing video matrix views (1x1, 2x2, 3x3 and 4x4 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"
IsVisible="{Binding Visible}">
<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.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.VideoGridTappedCommand, Source={x:Reference BrowseItemsPage}}"
CommandParameter="{x:Reference VideoGrid}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>-->
</Grid>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>`
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.
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); } }
I load all 16 items first. Then I use visibility binding to show / hide the items based on the the number of views in the matrix. The number of new elements from OnElementChanged events is 2, 6, 12 and 16 for 1, 4, 9 and 16 views respectively. Why 2, 6, 12 instead of 1, 4 and 9? It seems there is an extra row hidden for the first three?
Please note that the number of surface holders created with Android surface view is always correct each time. When switching to 1, 4, 9 and 16 views, there are 1, 4, 9 and 16 surface holders created respectively.
This approach is easier than loading just 4 items first and then adding or removing accordingly. See Xamarin forms iOS UIView Renderer intermittent OnElementChanged in some cases: https://stackoverflow.com/questions/63107557/xamarin-forms-ios-uiview-renderer-intermittent-onelementchanged-in-some-cases
I tried to clear all ItemsSource items and then add new items. But the problem remains. It seems clearing / reinitializing biding items doesn't help. Please note that If I open a new page on changing view size, it works properly. I also tried to reinitialize the UIView using Init() of NSObject, Initialize(), or even Dispose() or set to null without success.
I tried the sample SimpleGalleryPage and observed similar behaviour. If I remove to n items, only these items have FlowItemDisappearing visibility changed events. I want all items have the disappearing event hence all will be new elements in iOS ViewRenderer after adding more items later. Is this possible? @daniel-luberda, can you please shed light on this?
@daniel-luberda, I believe you can offer your helping hand. I've stuck with this issue for long time.
In Android app, when changing the size of the matrix view, each view element / cell, regardless of the size, does FlowItemDisappearing and then FlowItemAppearing consistently. That's what we want to achieve the same on iOS app.
iOS app's behaviour is different, only some elements in the matrix view do disappearing and appearing. How can I change this behaviour?
In my iOS app, I compared the events in OnElementChanged in the renderer with the events in FlowItemDisappearing and FlowItemAppearing, and found discrepancies between them. For
example, after changing matrix size from 3x3 to 1 and then from 1 to 3x3, the following is observed: In OnElementChanged: 3x3-1: deleted first row, added new element 1, OK
1-3x3: recreated the elements 6-9 in 3rd row. The problems: 1. shouldn't need to update 3rd row; 2. the handles of element 2 and 3 were not updated, remaining 0.
3x3-1: deleted the elements in 2nd row. The video plays OK in single view. But the problem is now the handles for elements 2-6 become 0. It will be a problem for next transition 1-9.
In FlowListItem appearing / disappearing events:
3x3-1: disappearing view index 0-2 and appearing 0 (new element 1), correct
1-3x3: -(3-5), +(0-2), -(6,8), +(3-5), +(6-8), this says new elements in row 1, recreated elements in row 2 and 3. The problem: it doesn't match the events in OnElementChanged. It
appears OnElementChanged missed events.
3x3-1: -(0-2), +(0) new element 1, correct
My problem is the handles of the sub views will go out of sync at the end. If each view element / cell in the new matrix disappears and then appears, there will be no problem at all.
Every tile will be refreshed.
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.