-1

I have an awkward problem with GestureRecognizers on Xamarin WebView: Although the documentation any some questions/answers here and in Xamarin Forum say that WebView GestureRecognizers should all work, I can't get it to fire any event.

My XAML code looks like this:

<StackLayout BackgroundColor="LightGray" >
    <WebView x:Name="webView" VerticalOptions="FillAndExpand" >
        <WebView.GestureRecognizers>
            <SwipeGestureRecognizer Direction="Left" Swiped="onSwiped"/>
        </WebView.GestureRecognizers>
        <WebView.Source>
            <HtmlWebViewSource Html="{Binding HTML}" />
        </WebView.Source>
    </WebView>
</StackLayout>

Alternatives treid so far:

  • Same GestureRecognizer on the Title of the same page: works
  • Same GestureRecognizer on a ListView of another page: works
  • Tried Nuget package Vapolia.XamarinGestures which also didn't work on the webview
  • Tried to put the GestureRecoginzer on the StackLayout around the WebView: didn't work either.

Tried it on iOS device and simulator. Normally iOS should be the easy part here...

What I actually want to achieve: with a swipe left move forward to another (programatically defined) web page. I assume those gestures are somehow absorbed by the webview for regular navigation, but I was wondering why some examples would say that all gestures work on the webview.

An alternative could be to add that target webpage to the webview history stack on the "forward" path.. but not sure how to do that.

Anyone has some hints?

Kerry
  • 101
  • 1
  • 7

2 Answers2

1

You could use Custom Renderer to add the swipe event on specific platform. And handle them in Forms .

in Forms

create a CustomWebView

public class CustomWebView : WebView
{
    public event EventHandler SwipeLeft;
    public event EventHandler SwipeRight;

    public void OnSwipeLeft() =>
        SwipeLeft?.Invoke(this, null);

    public void OnSwipeRight() =>
        SwipeRight?.Invoke(this, null);
}

in Android


using Android.Content;
using Android.Views;
using App11;
using App11.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(CustomWebView), typeof(MyWebViewRenderer))]
namespace App11.Droid
{
    public class MyWebViewRenderer : WebViewRenderer
    {
        public MyWebViewRenderer(Context context) : base(context)
        {

        }

        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);

            Control.SetOnTouchListener(new MyOnTouchListener((CustomWebView)Element));
        }

    }

    public class MyOnTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
    {
        float oldX;

        float newX;

        CustomWebView myWebView;
        public MyOnTouchListener(CustomWebView webView)
        {
            myWebView = webView;
        }
        public bool OnTouch(Android.Views.View v, MotionEvent e)
        {
            if (e.Action == MotionEventActions.Down)
            {
                oldX = e.GetX(0);
            }
            if (e.Action == MotionEventActions.Up)
            {
                newX = e.GetX();

                if (newX - oldX > 0)
                {
                    myWebView.OnSwipeRight();
                }
                else
                {
                    myWebView.OnSwipeLeft();
                }
            }

            return false;
        }
    }
}

in iOS


using App11;
using App11.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using ObjCRuntime;

[assembly: ExportRenderer(typeof(CustomWebView), typeof(MyWebViewRenderer))]
namespace App11.iOS
{
    public class MyWebViewRenderer:WkWebViewRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if(e.NewElement!=null)
            {

                this.BackgroundColor = UIColor.Red;

                UISwipeGestureRecognizer leftgestureRecognizer = new UISwipeGestureRecognizer(this,new Selector("SwipeEvent:"));

                leftgestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;

                UISwipeGestureRecognizer rightgestureRecognizer = new UISwipeGestureRecognizer(this, new Selector("SwipeEvent:"));
                rightgestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Right;

                leftgestureRecognizer.Delegate = new MyWebViewDelegate();
                rightgestureRecognizer.Delegate = new MyWebViewDelegate();

                this.AddGestureRecognizer(leftgestureRecognizer);
                this.AddGestureRecognizer(rightgestureRecognizer);
            }


        }

        [Export("SwipeEvent:")]
        void SwipeEvent(UISwipeGestureRecognizer recognizer)
        {
            var webview = Element as CustomWebView;

            if(recognizer.Direction == UISwipeGestureRecognizerDirection.Left)
            {
                webview.OnSwipeLeft();
            }
            else if(recognizer.Direction == UISwipeGestureRecognizerDirection.Right)
            {
                webview.OnSwipeRight();
            }
        }


    }

    public class MyWebViewDelegate: UIGestureRecognizerDelegate
    {
        public override bool ShouldRecognizeSimultaneously(UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer)
        {
            return false;
        }
    }


}

Now you just need to use it like

<local:CustomWebView x:Name="browser"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" 
           SwipeLeft="browser_SwipeLeft"
           SwipeRight="browser_SwipeRight">

Lucas Zhang
  • 18,630
  • 3
  • 12
  • 22
  • Lucas, thanks alot for the detailed example - was too busy to look into it so far.. I wanted to avoid a custom renderer, but I have another problem – Kerry Jun 16 '20 at 09:40
  • WebView in forms will block some GestureRecognizer .So use Custom Renderer is the best way . – Lucas Zhang Jun 16 '20 at 09:42
  • I wanted to avoid a custom renderer, but I have another problem now with the forms webview: I am parsing remote HTML to the Xamarin forms webview (webviewsource.Html = "..") which includes locally saved images. On the simulator it works fine, but on actual device I only see empty boxes (no error ? like missing image). I assume I need to set "allowingReadAccessToURL" to be able to load those local images and therefore need the custom viewcontroller again... Or is that also possible just in the renderer? – Kerry Jun 16 '20 at 09:46
  • They are different problems , you could create a new thread with more details so that we can help you better . And don't forget to accept my answer if it helps you , which will help more people who has similar issue . – Lucas Zhang Jun 16 '20 at 09:48
  • Yes, I solved the local image problem now with the respective custom renderer (not controller) as described here: https://stackoverflow.com/questions/39901982/wkwebview-fails-to-load-images-and-css-using-loadhtmlstring-baseurl/62407993#62407993 and will also implement your GestureRecognizer now. – Kerry Jun 16 '20 at 12:05
  • One more question on the gesturerecognizer: I've added your examples to my custom webview, but there's on inconsistency: in the XAML you link the events to "browser_SwipeLeft", but how does this link to the OnSwipeLeft() function of the CustomWebView? In my codebehind, I added an event handler to "browser_SwiepLeft" to call OnSwipeLeft() but nothing gets fired. Where am I wrong? – Kerry Jun 16 '20 at 12:30
  • in `browser_SwiepLeft` you need to handle the logic when swipe left (for example navigate to last page) . – Lucas Zhang Jun 16 '20 at 12:32
  • You don't need to call `OnSwipeLeft()` any more in your code . It had been invoked in custom renderer . – Lucas Zhang Jun 16 '20 at 12:32
0

There was an additional trick to make it finally work. All the above (correct) solution was ignored due to my Xamarin MasterDetailPage setup.

This was capturing all horizontal swipes and not putting them through to the HybridWebView.

MasterDetailPage.IsGestureEnabled = false;

finally fixed it and enabled the swipe gestures in my WebView.

Kerry
  • 101
  • 1
  • 7