SkiaSharp icon indicating copy to clipboard operation
SkiaSharp copied to clipboard

[BUG] SKTouchAction.Cancelled not issued on iOS when SkCanvasView is in a ScrollView

Open ghost opened this issue 5 years ago • 17 comments

Description

[On iOS] When a SkCanvasView is in a ScrollView, and the EnableTouchEvents property is set to 'true', the touch event is not cancelled (i.e.: SkTouchAction.Cancelled is not invoked) if the ScrollView is scrolled while touching the SkCanvasView. Neither is the SkTouchAction.Released event. Everything works fine on Android.

Code

See attached sample project.

Expected Behavior

SKTouchAction.Cancelled event invoked when the container is scrolled while touching the SkCanvasView.

Actual Behavior

Event is not invoked on iOS. Works fine on Android. Not tested on other platforms.

Basic Information

  • Version with issue: 1.68.1
  • Last known good version: unknown
  • IDE: VS2019 Mac/Windows
  • Platform Target Frameworks:
    • Xamarin.Forms

Reproduction Link

TestSkiaSharp.zip

ghost avatar Dec 03 '19 10:12 ghost

Well, after further investigations this behavior seems to be related to iOS and not to SkiaSharp itself. In this particular scenario, the underlying scrollview, as soon as the scroll begins, invokes the method 'touchesCancelled' of the view and not the GestureRecognizer's. This is why the SKCanvasView does not receive it. But the native view does. I guess something can be made at that level.

ghost avatar Dec 05 '19 13:12 ghost

Hey @MaurizioPanzica, I am currently running into this problem. I need a ListView that contains SKCanvasViews with touch handling. Have you been able to find a workaround?

daltonks avatar Jun 28 '20 20:06 daltonks

@daltonks Unfortunately not. Since then the project has been put on hold because of Covid and I've been working on another one. I guess it will be resumed in the near future. Rest assured that I will post the workaround, should I find one.

Cheers, M.

mpanzica avatar Jun 28 '20 22:06 mpanzica

@mpanzica Thanks for the reply :) I'm kind of led to believe that Xamarin is mucking up the touches somehow, because as far as I can tell, TouchesCancelled should be propagated to gesture recognizers when a scroll view sends the event.

daltonks avatar Jun 28 '20 22:06 daltonks

Nope, if I recall well Xamarin was not guilty at all. The "cancelled" event never reaches the gesture recognizer handler... hence it is not propagated any further. I will be able to resume the project as soon as I'll be allowed to go back to office. I can't access the client's repository from home. :-)

mpanzica avatar Jun 28 '20 22:06 mpanzica

Thanks for the info @mpanzica ! I am currently thinking that this might be because SKTouchHandler doesn't change its UIGestureRecognizer.State.

In your custom subclass, implement whatever methods you need to process events. For example, if your gesture consists of touch events, implement the touchesBegan(:with:), touchesMoved(:with:), touchesEnded(:with:), and touchesCancelled(:with:) methods. Use incoming events to update the state property of your gesture recognizer. UIKit uses the gesture recognizer states to coordinate interactions with other objects in your interface.

https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer https://github.com/mono/SkiaSharp/blob/master/source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms.iOS/SKTouchHandler.cs

daltonks avatar Jun 29 '20 00:06 daltonks

Nope, that idea led nowhere 🙃. Setting the State stops the scroll view from receiving the touches.

daltonks avatar Jun 29 '20 01:06 daltonks

@mpanzica I found a way that seems to work! If you keep track of the touches with HashSet<UITouch> _touches, you can manually cancel the ones in the gesture recognizer's Reset method.

https://gist.github.com/daltonks/f01cf125b580ca8b7b9213413f9299a5

daltonks avatar Jun 29 '20 20:06 daltonks

Thanks for sharing, Dalton! It's night over here... I will give your solution a try tomorrow morning before anything else! :-)

Il lun 29 giu 2020, 22:24 Dalton Spillman [email protected] ha scritto:

@mpanzica https://github.com/mpanzica I found a way that seems to work! If you keep track of the touches with HashSet<UITouch> _touches, you can manually cancel the ones in the gesture recognizer's Reset method.

https://gist.github.com/daltonks/f01cf125b580ca8b7b9213413f9299a5

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mono/SkiaSharp/issues/1048#issuecomment-651341248, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGHODQOL6O3VQS5LGW3VGQ3RZD2AHANCNFSM4JUWBKWQ .

mpanzica avatar Jun 29 '20 20:06 mpanzica

Cool! I made a repo based off of your reproduction .zip: https://github.com/daltonks/FixedSKTouchHandler

Tabs/spaces got all screwy, and I did strange things to use my own SKTouchHandler, but you can see the majority of the changes here: https://github.com/daltonks/FixedSKTouchHandler/blob/master/TestSkiaSharp.iOS/Renderers/TestSKCanvasViewRenderer.cs

The key part is keeping track of the touches and overriding UIGestureRecognizer.Reset.

daltonks avatar Jun 29 '20 21:06 daltonks

Thanks for all the back and forth on this. I'll try to get this into the next release.

Has anyone been using this? Do all the touches that are started get the opposite finish/cancel event? Is there a potential for a memory leak or persistent touch objects?

mattleibow avatar Oct 13 '20 22:10 mattleibow

I've been using this successfully in production for a bit over 3 months now. I haven't seen any touch issues, but it would be good to look at how it behaves in a ScrollView as well as when iOS gestures take over (like minimizing the app, and maybe some other ones).

daltonks avatar Oct 13 '20 23:10 daltonks

OK, thanks for the comment. I'll try a few things.

mattleibow avatar Oct 13 '20 23:10 mattleibow

I fixed this issue a while ago on a UIGestureRecognizer by returning false to the ShouldRecognizeSimultaneously delegate, and also triggering the Cancelled at the same time.

I'm trying to switch over to use the SkiaSharp OnTouch and now hitting this issue. This may be a simpler fix than tracking the touches.

DuncWatts avatar Dec 15 '20 09:12 DuncWatts

@DuncWatts Would returning false for ShouldRecognizeSimultaneously completely stop scrolling if the canvas was in a scroll view and you pressed on the SkiaSharp view? That is not what I typically need, but I could see a use case for that.

daltonks avatar Dec 15 '20 15:12 daltonks

No, it allows the scroll gesture on the scroll view to take priority and cancels the touch event on the canvas, which is the functionality I was after.

DuncWatts avatar Dec 15 '20 16:12 DuncWatts

@DuncWatts I tried ShouldRecognizeSimultaneously = (recognizer, gestureRecognizer) => false; in my example project https://github.com/daltonks/FixedSKTouchHandler, but it didn't call Cancelled on scrolling. I could be doing it wrong though.

https://github.com/daltonks/FixedSKTouchHandler/blob/526c035459193d1d4d600e500653446ac510f646/TestSkiaSharp.iOS/Renderers/TestSKCanvasViewRenderer.cs#L47

daltonks avatar Dec 16 '20 00:12 daltonks