Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Xamarin form Community toolkit Media element Controls Not showing up in iOS 16 #1927

Open
pjsoftcoUser opened this issue Oct 14, 2022 · 32 comments
Labels
bug Something isn't working. Breaky break.

Comments

@pjsoftcoUser
Copy link

MediaElement: ShowsPlaybackControls="True" not showing controls in IOS, please fix!
image

@pjsoftcoUser pjsoftcoUser added the bug Something isn't working. Breaky break. label Oct 14, 2022
@bijington
Copy link
Contributor

@pjsoftcoUser we have a template that asks for a lot more information to aid understanding what the issue is and ultimately be able to reproduce it. Can you please use that template to provide the information that we require?

@pjsoftcoUser
Copy link
Author

pjsoftcoUser commented Oct 14, 2022

Xamarin forms V5.0.0.2515

IDE: VS2022

Control: xamarin.communitytoolkit.ui.views.mediaElement

Version: xamarin.communityToolkit 2.0.5

Android works just fine

IOS not showing the control panel for the video, testing on latest IOS 16 (no difference on an emulator or actual device, both not showing the controls)

Let me know if you need more info.

@pjsoftcoUser
Copy link
Author

maybe this is also related:
https://developer.apple.com/forums/thread/711360

@lpv-1902
Copy link

Can also confirm this doesn't work on my end.

@fbcom92
Copy link

fbcom92 commented Oct 19, 2022

Same problem for me. Ok for Android, but no controls at all on IOS16 and Xcode 14.
Xamarin forms V5.0.0.2515
IDE: VS2022 on Mac
Control: xamarin.communitytoolkit.ui.views.mediaElement
Version: xamarin.communityToolkit 2.0.5

@fbcom92
Copy link

fbcom92 commented Oct 25, 2022

@bijington, can you update this one please ?

@stevetranby
Copy link

stevetranby commented Oct 28, 2022

Can confirm that the same build of an app running on iOS 15 works displaying controls, but running on iOS 16 the controls are not visible. The play/pause functionality still works if you touch right in the center of the video. Possibly the controls are just hidden even if they're active?

Edit: it might also be due to the warning with 'showsplaybackcontrols' if Xamarin iOS Custom Renderer is setting this after the platform-specific element is already created?
https://developer.apple.com/documentation/avkit/avplayerviewcontroller/1615824-showsplaybackcontrols

@fbcom92
Copy link

fbcom92 commented Oct 28, 2022

@stevetranby I agree with you, with a little patience you can play with the slider at the bottom of the screen. So the controls are active, but not visible.

I know that all efforts are directed towards MAUI, but the correction should not be so complex and while waiting for MAUI to be 100% operational, it would be nice if these kinds of bugs were fixed.

@pjsoftcoUser
Copy link
Author

pjsoftcoUser commented Oct 28, 2022

Yet, no attention from them!!!

@bijington
Copy link
Contributor

I do believe a similar issue has been observed while adding in the Media playback functionality for the MAUI Community toolkit. I suspect whatever fix is introduced there will likely make its way over to this toolkit but I don't know when that would be.

It's correct that the team doesn't have the bandwidth to cover everything in the XCT so we sadly can't fix everything and certainly not quickly. We do of course accept PRs as the C does stand for Community.

@fbcom92
Copy link

fbcom92 commented Oct 29, 2022

Hi @bijington, and thanks for your kind answer.
And as the C stands for Community, you will be pleased to know that if you add Shell.PresentationMode="ModalAnimated", the controls are visible on IOS16. For what it's worth, maybe it will be helpful to understand the bug for MAUI too.

@TrevorNa
Copy link

TrevorNa commented Dec 5, 2022

hi @fbcom92 Shell.PresentationMode="ModalAnimated" this not work for me
is iOS 16 issue resolved yet or no ?

@KindJoy
Copy link

KindJoy commented Dec 5, 2022

@TrevorNa I don't believe it's been tackled within XCT yet. I had a poke around the Maui repo and from the comments by @jfversluis it looks like this commit appears to address the issue for Maui:

CommunityToolkit/Maui@68d4004

If that's all it is, the fix for XCT would probably be very similar (and pretty quick to do for someone who can compile the XCT repo?). I have a Xamarin.Forms project, which relies on MediaElement, and I've had to write my own transport controls management because of this change months ago in iOS 16.

(Edit: having had a look further, am not sure if this issue might need changes that impact further upstream, like to either Xamarin.Forms or the xamarin-macios repos, as it looks like most view controller stuff is handled there. The core of the issue is that the av player view controller needs to be attached to the parent view controller in order for the transport controls to be displayed in iOS 16. Looks like it was an easier fix in Maui due to how all that view stuff is organised.)

@AGusty
Copy link

AGusty commented Dec 18, 2022

Create a custom renderer.

`

public class MyMediaElementRenderer : MediaElementRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<MediaElement> e)
    {
        base.OnElementChanged(e);


        if (!UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
            return;


        if (e.NewElement != null)
        {
            avPlayerViewController.View.RemoveFromSuperview();

            SetNativeControl();
        }
    }

    private void SetNativeControl()
    {
        var viewController = GetCurrentUIViewController() ?? throw new NullReferenceException("ViewController can't be null.");

        _ = viewController.View ?? throw new NullReferenceException(nameof(viewController.View));

        var insets = viewController.View.SafeAreaInsets;
        avPlayerViewController.AdditionalSafeAreaInsets = new UIEdgeInsets(insets.Top * -1, insets.Left, insets.Bottom * -1, insets.Right);

        viewController.View.AddSubview(avPlayerViewController.View);

        AddSubview(avPlayerViewController.View);
    }

    private UIViewController GetCurrentUIViewController()
    {
        var window = UIApplication.SharedApplication.KeyWindow ?? UIApplication.SharedApplication.Windows?.FirstOrDefault();
        if (window == null)
            return null;

        var viewController = window.RootViewController;

        while (viewController.PresentedViewController != null)
        {
            viewController = viewController.PresentedViewController;
        }

        return viewController;
    }
}


`

@unamed000
Copy link

Is this solved? Otherwise, do we have any workaround yet?

@KindJoy
Copy link

KindJoy commented Jan 7, 2023

@unamed000 I didn't get a chance yet to try @AGusty's solution so I can't say for sure that this does it, although it looks like it might. Ultimately in my project I had to reinvent the media player completely in order to make it work on iOS 16, which isn't something someone investing in an MS-backed platform should be worrying about. I lost many billable hours because the xamarin / forms support wasn't there. Suffice it to say that I won't be evaluating any of these things, e.g. Maui etc., for work going forward. If a project is genuinely community-based then you take that into account, but when you have a major player supposely invested in something, including their employees on MS time promulgating it on social platforms, well, ...

@pjsoftcoUser
Copy link
Author

This is really disappointing!

@bijington
Copy link
Contributor

@pjsoftcoUser what is disappointing?

It doesn't appear that we have noted it here but I am sure we discussed in during a stand-up. The plan is to fix the issue as part of the work being done in the .NET MAUI Community Toolkit at: CommunityToolkit/Maui#667 once that was proved to be in and working then the fix would be pushed across to the Xamarin Community Toolkit.

@pjsoftcoUser
Copy link
Author

The fact that this issue is happening for almost 4 months now and the fix is still not out.

@bijington
Copy link
Contributor

This is a toolkit built and maintained by members of the community in their own free time and we each have a limited amount of time that we can dedicate to it. None of us get paid to do this work.

That being said it is completely open to more members of the community contributing towards the project. If you or any other member that this affects would be willing to submit a fix then we would happily review it and release a new version with the fix inside.

@pjsoftcoUser
Copy link
Author

Thanks for the explanation, i'm sure this can invite the people interested in contributing.

@KindJoy
Copy link

KindJoy commented Jan 13, 2023

For the record it looks like @martijn00 just updated XamarinMediaManager with essentially the fix that was proposed last month. So if you're using this it may be possible to incorporate it in your setup before it eventually if ever filters down to Xamarin Forms:

Baseflow/XamarinMediaManager@1c7141e

Bit late for my project, but am sure welcome for many so good on ya. iOS 16 went public last September and I eventually had to recode my own media player from scratch for a client project that suddenly had no controls on the media people had paid for. Guess it was tricky for a lot of people who bought into a cross-platform MS toolset which was partially-switched to being community based after we'd invested in using it. There's a moral in there somewhere.

@shintaroo-hub
Copy link

shintaroo-hub commented Jan 16, 2023

A bit counter intuitive, but I made a hack to make it work.

1: Create an empty container (let's say a stacklayout)
2: on the code behind, do

           await Task.Delay(500); //this is a hack to make the controls on the video appear
           MediaContainer.Children.Add(
               new MediaElement()
               {
                   HeightRequest = 250,
                   Source = "media_link here"
               });

3: the element should get a 500ms delay to show up but the media player now has the controls

@burrowj
Copy link

burrowj commented Jan 22, 2023

Unfortunately, the fix for XamarinMediaManager only applies to net7 framework

@burrowj
Copy link

burrowj commented Feb 2, 2023

Agusty, that fixed the issue thanks!

@Seesi
Copy link

Seesi commented Feb 13, 2023

Create a custom renderer.

`

public class MyMediaElementRenderer : MediaElementRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<MediaElement> e)
    {
        base.OnElementChanged(e);


        if (!UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
            return;


        if (e.NewElement != null)
        {
            avPlayerViewController.View.RemoveFromSuperview();

            SetNativeControl();
        }
    }

    private void SetNativeControl()
    {
        var viewController = GetCurrentUIViewController() ?? throw new NullReferenceException("ViewController can't be null.");

        _ = viewController.View ?? throw new NullReferenceException(nameof(viewController.View));

        var insets = viewController.View.SafeAreaInsets;
        avPlayerViewController.AdditionalSafeAreaInsets = new UIEdgeInsets(insets.Top * -1, insets.Left, insets.Bottom * -1, insets.Right);

        viewController.View.AddSubview(avPlayerViewController.View);

        AddSubview(avPlayerViewController.View);
    }

    private UIViewController GetCurrentUIViewController()
    {
        var window = UIApplication.SharedApplication.KeyWindow ?? UIApplication.SharedApplication.Windows?.FirstOrDefault();
        if (window == null)
            return null;

        var viewController = window.RootViewController;

        while (viewController.PresentedViewController != null)
        {
            viewController = viewController.PresentedViewController;
        }

        return viewController;
    }
}


`

Hi @AGusty Didn't work for me. I'm I missing something?

@Seesi
Copy link

Seesi commented Feb 13, 2023

Unfortunately, the fix for XamarinMediaManager only applies to net7 framework

@AGusty is this the reason why?

@tonieichelkraut
Copy link

Create a custom renderer.

`

public class MyMediaElementRenderer : MediaElementRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<MediaElement> e)
    {
        base.OnElementChanged(e);


        if (!UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
            return;


        if (e.NewElement != null)
        {
            avPlayerViewController.View.RemoveFromSuperview();

            SetNativeControl();
        }
    }

    private void SetNativeControl()
    {
        var viewController = GetCurrentUIViewController() ?? throw new NullReferenceException("ViewController can't be null.");

        _ = viewController.View ?? throw new NullReferenceException(nameof(viewController.View));

        var insets = viewController.View.SafeAreaInsets;
        avPlayerViewController.AdditionalSafeAreaInsets = new UIEdgeInsets(insets.Top * -1, insets.Left, insets.Bottom * -1, insets.Right);

        viewController.View.AddSubview(avPlayerViewController.View);

        AddSubview(avPlayerViewController.View);
    }

    private UIViewController GetCurrentUIViewController()
    {
        var window = UIApplication.SharedApplication.KeyWindow ?? UIApplication.SharedApplication.Windows?.FirstOrDefault();
        if (window == null)
            return null;

        var viewController = window.RootViewController;

        while (viewController.PresentedViewController != null)
        {
            viewController = viewController.PresentedViewController;
        }

        return viewController;
    }
}


`

Awesome! For me the custom renderer works!

@Seesi
Copy link

Seesi commented Mar 9, 2023

Create a custom renderer.
`

public class MyMediaElementRenderer : MediaElementRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<MediaElement> e)
    {
        base.OnElementChanged(e);


        if (!UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
            return;


        if (e.NewElement != null)
        {
            avPlayerViewController.View.RemoveFromSuperview();

            SetNativeControl();
        }
    }

    private void SetNativeControl()
    {
        var viewController = GetCurrentUIViewController() ?? throw new NullReferenceException("ViewController can't be null.");

        _ = viewController.View ?? throw new NullReferenceException(nameof(viewController.View));

        var insets = viewController.View.SafeAreaInsets;
        avPlayerViewController.AdditionalSafeAreaInsets = new UIEdgeInsets(insets.Top * -1, insets.Left, insets.Bottom * -1, insets.Right);

        viewController.View.AddSubview(avPlayerViewController.View);

        AddSubview(avPlayerViewController.View);
    }

    private UIViewController GetCurrentUIViewController()
    {
        var window = UIApplication.SharedApplication.KeyWindow ?? UIApplication.SharedApplication.Windows?.FirstOrDefault();
        if (window == null)
            return null;

        var viewController = window.RootViewController;

        while (viewController.PresentedViewController != null)
        {
            viewController = viewController.PresentedViewController;
        }

        return viewController;
    }
}


`

Hi @AGusty Didn't work for me. I'm I missing something?

After a Xamarin iOS update, it works now.

@kelltom
Copy link

kelltom commented Mar 20, 2023

After a Xamarin iOS update, it works now.

from the above comment did not work for me


One workaround I've found is to add the MediaElement to a container control at runtime. For instance, in my code behind, I subscribe to my view model's .PropertyChanged event, and in the handler, it listens for changes in a VideoSource property. When the video source is set, I create a new MediaElement and add it as a child to a StackLayout container. This is a bit specific to my scenario, but I think the idea is clear enough.

XAML:

<StackLayout x:Name="MediaContainer" ... >

</StackLayout>

In code behind:

        protected override void OnAppearing()
        {
            base.OnAppearing();
            if (BindingContext is INotifyPropertyChanged viewModel)
            {
                viewModel.PropertyChanged += OnViewModelPropertyChanged;
            }
            ...
        }
        private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(MyViewModel.VideoSource))
            {
                if (_myViewModel.VideoSource == null)
                {
                    MediaContainer.Children.Clear();
                    return;
                }

                // If VideoSource was set, make a new MediaElement
                var mediaElement = new MediaElement
                {
                    Source = _myViewModel.VideoSource,
                    ...
                };

                MediaContainer.Children.Add(mediaElement);
            }
        }

@willtam
Copy link

willtam commented Apr 18, 2023

After a Xamarin iOS update, it works now.

from the above comment did not work for me

One workaround I've found is to add the MediaElement to a container control at runtime. For instance, in my code behind, I subscribe to my view model's .PropertyChanged event, and in the handler, it listens for changes in a VideoSource property. When the video source is set, I create a new MediaElement and add it as a child to a StackLayout container. This is a bit specific to my scenario, but I think the idea is clear enough.

XAML:

<StackLayout x:Name="MediaContainer" ... >

</StackLayout>

In code behind:

        protected override void OnAppearing()
        {
            base.OnAppearing();
            if (BindingContext is INotifyPropertyChanged viewModel)
            {
                viewModel.PropertyChanged += OnViewModelPropertyChanged;
            }
            ...
        }
        private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(MyViewModel.VideoSource))
            {
                if (_myViewModel.VideoSource == null)
                {
                    MediaContainer.Children.Clear();
                    return;
                }

                // If VideoSource was set, make a new MediaElement
                var mediaElement = new MediaElement
                {
                    Source = _myViewModel.VideoSource,
                    ...
                };

                MediaContainer.Children.Add(mediaElement);
            }
        }

I can confirm that this approach worked too by dynamically adding the MediaElement into a container in code-behind

@devperson
Copy link

After a Xamarin iOS update, it works now.

from the above comment did not work for me

One workaround I've found is to add the MediaElement to a container control at runtime. For instance, in my code behind, I subscribe to my view model's .PropertyChanged event, and in the handler, it listens for changes in a VideoSource property. When the video source is set, I create a new MediaElement and add it as a child to a StackLayout container. This is a bit specific to my scenario, but I think the idea is clear enough.

XAML:

<StackLayout x:Name="MediaContainer" ... >

</StackLayout>

In code behind:

        protected override void OnAppearing()
        {
            base.OnAppearing();
            if (BindingContext is INotifyPropertyChanged viewModel)
            {
                viewModel.PropertyChanged += OnViewModelPropertyChanged;
            }
            ...
        }
        private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(MyViewModel.VideoSource))
            {
                if (_myViewModel.VideoSource == null)
                {
                    MediaContainer.Children.Clear();
                    return;
                }

                // If VideoSource was set, make a new MediaElement
                var mediaElement = new MediaElement
                {
                    Source = _myViewModel.VideoSource,
                    ...
                };

                MediaContainer.Children.Add(mediaElement);
            }
        }

@kelltom thanks your workaround works

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working. Breaky break.
Projects
None yet
Development

No branches or pull requests