PaulVrugt
PaulVrugt

Reputation: 1882

Need event when UIWebView scrolls and option to cancel the scroll

We are building an app using xamarin.forms, which has a certain page that displays a webview. We use custom renderers for the webview to enable some features that are not implemented in the xamarin.forms webview. In the IOS version of the app, this webview is an UIWebView control. What I need, is a way to catch touch start, move and end events on the UIWebView control and be able to calculate the distance that the touch has moved. Furthermore, I need to be able to cancel the touchmove event from firing in the UIWebView, so the content doesn't scroll until I want it to.

I've already accomplished this in the android version by using the Touch eventhandler of the android webview. It can do exactly what i want, and the touch event has a "handled" property that can be used to avoid the event from triggering in the webview.

I've found all kinds ways to catch scroll events in IOS. The most promising seems to be implementing a gestureRecognizer to the UIWebView's scrollview. This way I can catch touch start, move and end events. However, I have a few problems with this implementation:

The way i currently attach the gesture recognizer:

var test = new MyRecognizer();
webView.ScrollView.AddGestureRecognizer(test);

The gesture recognizer:

    public class MyRecognizer : UIGestureRecognizer
    {
        private nfloat _startY;

        public MyRecognizer()
        {
            this.CancelsTouchesInView = true;
        }

        public override void TouchesBegan(NSSet touches, UIEvent evt)
        {
            base.TouchesBegan(touches, evt);
            var location = (touches.AnyObject as UITouch).LocationInView(View);
            _startY = location.Y;
        }

        public override void TouchesMoved(NSSet touches, UIEvent evt)
        {
            base.TouchesMoved(touches, evt);
            var location = (touches.AnyObject as UITouch).LocationInView(View);
            var intCurrentY = location.Y;

            if(someCondition)
            {
                //cancel the event somehow to prevent the webview from scrolling

                //a post mentioned this should do the trick, but it doesnt work:
                this.Enabled = false;
                this.Enabled = true;
            }
        }

        public override void TouchesEnded(NSSet touches, UIEvent evt)
        {
            base.TouchesEnded(touches, evt);
        }
    }

Am I on the right track here? Why do the events stop firing? And how can I cancel the event to prevent the webview from scrolling in SOME cases (I don't want to ALWAYS disable scrolling on webview)

Upvotes: 1

Views: 788

Answers (1)

PaulVrugt
PaulVrugt

Reputation: 1882

I figured it out myself. At least part of it. The events stopped firing because during the move, the default UIPanGestureRecognizer of the UIWebView's ScrollView captured the gesture. This is the code of my own recognizer that does the trick in capturing the events, and doesn't stop after a while:

public class TopBottomBarScrollRecognizer : UIPanGestureRecognizer
{
    private nfloat _startY;
    private nfloat _startX;
    private HybridWebView _webView;
    private UIWebView _nativeWebView;

    public TopBottomBarScrollRecognizer()
    {
        this.DelaysTouchesBegan = false;
        this.DelaysTouchesEnded = false;
        this.CancelsTouchesInView = false;

        //make sure the recognizer can work together with other recognizers
        this.ShouldRecognizeSimultaneously = (a, b) => true;
    }

    public TopBottomBarScrollRecognizer(HybridWebView webView, UIWebView nativeWebView) : this()
    {
        _webView = webView;
        _nativeWebView = nativeWebView;
    }

    public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);
        var location = (touches.AnyObject as UITouch).LocationInView(_nativeWebView);
        _startY = location.Y;
        _startX = location.X;
    }

    public override void TouchesMoved(NSSet touches, UIEvent evt)
    {
        base.TouchesMoved(touches, evt);
        var location = (touches.AnyObject as UITouch).LocationInView(_nativeWebView);
        var deltaY = (float)(double)(location.Y - _startY);
        var deltaX = (float)(double)(location.X - _startX);
        _webView.scrollOccurred(deltaX, deltaY);
    }

    public override void TouchesEnded(NSSet touches, UIEvent evt)
    {
        base.TouchesEnded(touches, evt);
        _webView.scrollEndOccurred();
    }
}

The above solves part 1 of the question. The question on how to prevent the default recognizer to temporarily stop is not that important anymore, since the behavior as it is now seems to work fine.

Upvotes: 1

Related Questions