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

[Bug] SwipeView is too sensitive - opens when scrolling #12518

Closed
Tommigun1980 opened this issue Oct 19, 2020 · 27 comments · Fixed by #12758
Closed

[Bug] SwipeView is too sensitive - opens when scrolling #12518

Tommigun1980 opened this issue Oct 19, 2020 · 27 comments · Fixed by #12758
Assignees
Labels
a/collectionview a/swipeview in-progress This issue has an associated pull request that may resolve it! p/iOS 🍎 t/bug 🐛

Comments

@Tommigun1980
Copy link

Tommigun1980 commented Oct 19, 2020

Hi.

Issue one: SwipeView is great. I use it for deleting chat messages and leaving chat rooms, by the user swiping left on a message or a chat. However, there seems to be absolutely no threshold for the swipe to happen, as it constantly opens whenever I scroll the list of chat messages or chat rooms. It is almost impossible to swipe perfectly vertically, so any even absolutely slight horizontal movement opens the swipe menu. The threshold value should be at least equal to a tap gesture recognisers default threshold value (and preferably scriptable) - because currently both a tap and swipe start open overlap.

This might sound like a small thing, but it completely breaks the experience as almost every scroll opens the swipe menu.

I think this would be a small fix that would make it immensely more usable so I am eagerly awaiting a fix for it.

Tested with latest XF 5 pre-release on iOS. I used the SwipeView in a bindable stack layout, enclosed in a scroll view.

Thank you.

@Tommigun1980 Tommigun1980 added s/unverified New report that has yet to be verified t/bug 🐛 labels Oct 19, 2020
@Tommigun1980
Copy link
Author

Tommigun1980 commented Oct 19, 2020

Issue two: Another bug with the swipe menu, which is almost as bad, is that when you are scrolling a view and during that same scroll do a slight horizontal movement, the swipe menu starts opening.

If a vertical scroll is ongoing the swipe menu should not open. It should only open if the user starts with a horizontal swipe.

These issues are mostly apparent when using an actual device one-handedly, not so much with a mouse in the simulator.

@Tommigun1980
Copy link
Author

Tommigun1980 commented Oct 19, 2020

Issue three: A third bug with the swipe view is that very often when you start scrolling and the swipe menu accidentally opens (as outlined in the initial message) and you stop scrolling, the UI breaks in two possible ways (seemingly randomly)

  1. The item stays stuck in the position it would be as if the swipe view was visible, but it is not. Tapping, scrolling, nothing seems to bring it back.
  2. The UI completely locks up and a restart of the app is required.

So to clarify:

  1. You start scrolling a view vertically
  2. Due to the sensitivity of the swipe view it opens on the first item (from right in my case)
  3. You continue scrolling normally
  4. The swipe view either disappears at this stage or stays visible
  5. No matter if it disappears or not, the actual item stays on the left side, as if the swipe view item was visible
  6. The UI may lock up

I think these are all related and possibly the same issue, can't say for sure.

@Tommigun1980
Copy link
Author

Tommigun1980 commented Oct 19, 2020

I am positive that a repro with merely a list of items (enough to simulate a normal scrolling of content experience) and swipe view in all of them, when run on an actual device, is enough to repro all these issues. It is very important to do this on a real device, use it one handedly as people normally do and they should all be reproducible.

I also have a video that showcases all the above issues just by trying to scroll my scroll view. Please let me know where to send it in case it would be useful (don't want to post it publicly).

Thank you for a prompt fix!

@Tommigun1980
Copy link
Author

Tommigun1980 commented Oct 19, 2020

Issue four: It a swipe view is open and you try to invoke it again (in my case swipe left again on an already opened swipe view), its animation glitches. Expected behavior is that the open swipe view gesture wouldn’t do anything if it’s already open.

@Tommigun1980
Copy link
Author

Tommigun1980 commented Oct 19, 2020

Fix suggestions (in order of impact):

  1. Add some threshold “resistance” before a swipe view goes into the above “opening swipe item” state. Resistance should be at least equal to the default tap gesture recognizer's fudge factor, and preferably scriptable.
  2. Fix the state where a previously opened swipe view stays “open”, and the UI locks up.
  3. Ignore panning in the direction that opens a swipe menu if it is already open, to prevent issue 4.
  4. Lock the panning axes: if the user is scrolling vertically, no horizontal movement during that swipe should invoke the swipe view. Conversely if the user is opening the swipe menu, no vertical movement should scroll the view.

@jsuarezruiz Hi, I saw you added the collection view tag. This is not exclusive to collection views, I use bindable stack layouts with a scroll view. In fact I haven’t tested this with a collection view (updated the OP to say this).

@Kapusch
Copy link

Kapusch commented Oct 21, 2020

Same as @Tommigun1980 , I do also encountered many times the "UI break" (not able to scroll anymore until I swipe an item).

My configuration :

  • SwipeView nested in a CollectionView
  • XF 4.8.0.1560
  • iOS 14
  • VS for Mac

This is how I'm able to reproduce it :

  1. Start swiping horizontally (I generally swipe half long of the phone width)
  2. Keep your finger pressed on the swiped item, and start scrolling up & down
  3. Release your finger

Up to this point :

  • there is a high percent of not being able to scroll anymore,
  • if I want to unlock my collectionview, I must swipe on an item before to scroll.

@PureWeen PureWeen added the blocker Issue blocks next stable release. Prioritize fixing and reviewing this issue. label Nov 2, 2020
@PureWeen PureWeen removed the blocker Issue blocks next stable release. Prioritize fixing and reviewing this issue. label Nov 5, 2020
@Tommigun1980
Copy link
Author

Tommigun1980 commented Nov 20, 2020

Thank you @jsuarezruiz for fixing the lock up issue. Is there a PR for the sensitivity issue yet or do you think it will also be fixed hopefully soonish? It is a real problem that panning a collection constantly opens the swipe view by accident, as it's so hard to do pixel perfect vertical swipes with your fingers on an actual device? It is almost inevitable to constantly open the swipe items by accident :(

Thank you so much for working so hard to fix these issues, we really appreciate it!

@jsuarezruiz jsuarezruiz added the in-progress This issue has an associated pull request that may resolve it! label Nov 24, 2020
@kaniosm
Copy link

kaniosm commented Jan 3, 2021

After a quick look at the current SwipeView android render, I noticed the constant "SwipeMinimumDelta".
I suspect this was used to prevent the scroll issue referred above since I can see it is used in OnParentScrolled methods.
At first, I believe the same can be used in ShouldInterceptTouch to release the touch event to the parent in case the delta is too small.
Later, the SwipeMinimumDelta constant or a new property can be exposed to the forms control to allow developers to define this value according to their needs.
e.g.

`bool ShouldInterceptTouch(MotionEvent e)
{
if (_initialPoint == null)
return false;

		var interceptPoint = new Point(e.GetX() / _density, e.GetY() / _density);

		var diffX = interceptPoint.X - _initialPoint.X;
		var diffY = interceptPoint.Y - _initialPoint.Y;

		//Check if swipe delta is too small and release touch
		if (Math.Abs(diffX) < SwipeMinimumDelta && Math.Abs(diffY) < SwipeMinimumDelta)
			return false;

		SwipeDirection swipeDirection;

		if (Math.Abs(diffX) > Math.Abs(diffY))
			swipeDirection = diffX > 0 ? SwipeDirection.Right : SwipeDirection.Left;
		else
			swipeDirection = diffY > 0 ? SwipeDirection.Down : SwipeDirection.Up;

		var items = GetSwipeItemsByDirection(swipeDirection);

		if (items == null || items.Count == 0)
			return false;

		return ShouldInterceptScrollChildrenTouch(swipeDirection);
	}`

@Tommigun1980
Copy link
Author

Hi!

This issue still persists, that when a view is scrolled the SwipeViews constantly pop up by accident. It is so bad I don't think I could use it in production yet.

Do you guys think this could be fixed at some point?

Thanks so much!

@kkppstudios
Copy link

Hi, just checking in on where this stands. This is still an issue on Android and I only see #12758 which is only targeting ios. Should a new request be opened?

@kaniosm
Copy link

kaniosm commented Feb 3, 2021

From what I understand the current issue is still open, therefore there is no need to open a new one.
This issue is open for quite some time now.
In my case, I cloned the Xamarin.Forms repo and I did the below changes to the Xamarin.Forms.Platform.Android/Renderers/SwipeViewRenderer class, which solved the issue for me.
I'm recompiling the assembly with every new release and I'm replacing the Xamarin.Forms.Platform.Android.dll in my Nuget cache folder with the compiled one, and therefore all my projects are updated with the new dll!
Line 238 on the sr2 Tag

//Check if swipe delta is too small and release touch

if (Math.Abs(diffX) < SwipeMinimumDelta && Math.Abs(diffY) < SwipeMinimumDelta) return false;

I hope this helps some of you, until this is taken care in a future release

@kkppstudios
Copy link

From what I understand the current issue is still open, therefore there is no need to open a new one.
This issue is open for quite some time now.
In my case, I cloned the Xamarin.Forms repo and I did the below changes to the Xamarin.Forms.Platform.Android/Renderers/SwipeViewRenderer class, which solved the issue for me.
I'm recompiling the assembly with every new release and I'm replacing the Xamarin.Forms.Platform.Android.dll in my Nuget cache folder with the compiled one, and therefore all my projects are updated with the new dll!
Line 238 on the sr2 Tag

//Check if swipe delta is too small and release touch

if (Math.Abs(diffX) < SwipeMinimumDelta && Math.Abs(diffY) < SwipeMinimumDelta) return false;

I hope this helps some of you, until this is taken care in a future release

This is a great work-around, thanks kaniosm. I haven't tried this yet as it adds a bit of overhead to my releases but I may have to resort to using it.

@decv86
Copy link

decv86 commented Feb 4, 2021

why can't it take so long to fix such seemingly simple things ... it just kills

@meJevin
Copy link

meJevin commented Mar 28, 2021

SwipeView is not ready for production use. UX is absolutely horrible.

@decv86
Copy link

decv86 commented Mar 28, 2021

@jsuarezruiz we need help! tell me this issue will be resolved?

@kkppstudios
Copy link

SwipeView is not ready for production use. UX is absolutely horrible.

Sadly, I agree. I've removed it from my production app in replace of just straight buttons. It ended up being a good decision especially since the SwipeView has not gotten enough love.

@KaeTar
Copy link

KaeTar commented Apr 20, 2021

@samhouts @jsuarezruiz do you know if there is some sort of ETA on this? I have 4 applications that have been holding release due to this one little issue. I am now working to remove SwipeView in order to get other features released.

@vhugogarcia
Copy link

SwipeView is not ready for production use. UX is absolutely horrible.

Sadly, I agree. I've removed it from my production app in replace of just straight buttons. It ended up being a good decision especially since the SwipeView has not gotten enough love.

Is there any other alternative or library that we can use for swipeview?

@vhugogarcia
Copy link

i found out this alternative, maybe it can work: https://github.com/AndreiMisiukevich/ContextMenu

@AlleSchonWeg
Copy link
Contributor

Hi @Tommigun1980 ,
do you find a workaround for this issue?

@lightwalker01
Copy link

Is there nothing new on this issue?
Thanks

@vhugogarcia
Copy link

Is there nothing new on this issue?

Thanks

There is a threshold property that you can use. I set it to 300 to make it compatible with Iphone 5 screens. It worked OK as temporary solution to this. I hope this get fixed properly on future xamarin.forms releases.

"SwipeView includes a Threshold property, of type double, which represents the number of device-independent units that trigger a swipe gesture to fully reveal swipe items."

@lightwalker01
Copy link

There is a threshold property that you can use. I set it to 300 to make it compatible with Iphone 5 screens. It worked OK as temporary solution to this. I hope this get fixed properly on future xamarin.forms releases.

"SwipeView includes a Threshold property, of type double, which represents the number of device-independent units that trigger a swipe gesture to fully reveal swipe items."

@vhugogarcia Thank you for your response!
But the Threshold property declare how many units are required to fully reveal swipe items, not how many units are required to initiate the swipe action (Sensitivity, i presume).

We have this issue that actually blocks the release of our app: on a CollectionView with tap gesture AND SwipeView on items, when the user tap over an item, the swipe action prevent the tap execution very often.

@vhugogarcia
Copy link

There is a threshold property that you can use. I set it to 300 to make it compatible with Iphone 5 screens. It worked OK as temporary solution to this. I hope this get fixed properly on future xamarin.forms releases.

"SwipeView includes a Threshold property, of type double, which represents the number of device-independent units that trigger a swipe gesture to fully reveal swipe items."

@vhugogarcia Thank you for your response!

But the Threshold property declare how many units are required to fully reveal swipe items, not how many units are required to initiate the swipe action (Sensitivity, i presume).

We have this issue that actually blocks the release of our app: on a CollectionView with tap gesture AND SwipeView on items, when the user tap over an item, the swipe action prevent the tap execution very often.

That is correct my friend. There is no solution for the sensitivity. However, that property helped me to define a 300 and prevented users to open it while scrolling. Give it a try. It is a temp solution only.

@Zwawoo
Copy link

Zwawoo commented Jul 31, 2021

I wrote a dirty workaround via CustomRenderer and Refelection for Android which implements the solution from @kaniosm
but without having to recompile Xamarin forms.
My first impression is that it works. Please check it out and give some feedback:
public class CustomSwipeView : SwipeView { }


using Android.Content;
using Android.Views;
using App.Util.CustomViews;
using System;

using System.Reflection;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using APointF = Android.Graphics.PointF;

[assembly: ExportRenderer(typeof(CustomSwipeView), typeof(App.Droid.CustomRenderer.DroidCustomSwipeViewRenderer))]
namespace App.Droid.CustomRenderer
{
    public class DroidCustomSwipeViewRenderer : SwipeViewRenderer
    {
        float _density;
        APointF _initialPoint;
        const float SwipeMinimumDelta = 10f;
        public DroidCustomSwipeViewRenderer(Context context) : base(context)
        {
            
        }
        protected override void OnElementChanged(ElementChangedEventArgs<SwipeView> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                _density = Resources.DisplayMetrics.Density;
            }
        }
        public override bool OnTouchEvent(MotionEvent e)
        {
            if (e.Action == MotionEventActions.Move && !ShouldInterceptTouch(e))
                return true;

            return base.OnTouchEvent(e);
        }

        public override bool DispatchTouchEvent(MotionEvent e)
        {
            if (e.Action == MotionEventActions.Down)
            {
                _initialPoint = new APointF(e.GetX() / _density, e.GetY() / _density);
            }

            return base.DispatchTouchEvent(e);
        }
        public override bool OnInterceptTouchEvent(MotionEvent e)
        {
            return ShouldInterceptTouch(e);
        }
        bool ShouldInterceptTouch(MotionEvent e)
        {
            if (_initialPoint == null)
                return false;

            var interceptPoint = new Point(e.GetX() / _density, e.GetY() / _density);

            var diffX = interceptPoint.X - _initialPoint.X;
            var diffY = interceptPoint.Y - _initialPoint.Y;

            if (Math.Abs(diffX) < SwipeMinimumDelta && Math.Abs(diffY) < SwipeMinimumDelta)
            {
                return false; 
            }
            SwipeDirection swipeDirection;

            if (Math.Abs(diffX) > Math.Abs(diffY))
                swipeDirection = diffX > 0 ? SwipeDirection.Right : SwipeDirection.Left;
            else
                swipeDirection = diffY > 0 ? SwipeDirection.Down : SwipeDirection.Up;

            SwipeItems items = null;
            var baseType = GetType().BaseType;
            if (baseType != null)
            {
                var getSwipeItemsByDirection = baseType.GetMethod("GetSwipeItemsByDirection",
                    BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SwipeDirection) }, null);
                items = (SwipeItems)getSwipeItemsByDirection.Invoke(this, new object[] { swipeDirection });
            }
            

            if (items == null || items.Count == 0)
                return false;

            var shouldInterceptScrollChildrenTouch = baseType.GetMethod("ShouldInterceptScrollChildrenTouch",
                    BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SwipeDirection) }, null);
            bool result = (bool)shouldInterceptScrollChildrenTouch.Invoke(this, new object[] { swipeDirection });

            return result;
        }
    }
}

Does this sensitivity problem also still exist on iOS? Haven't tried it out yet on iOS.

@kaniosm
Copy link

kaniosm commented Aug 1, 2021

Very good @Zwawoo! I've tested this approach and it works well...
I've made a minor change...
[assembly: ExportRenderer(typeof(SwipeView), typeof(App.Droid.CustomRenderer.DroidCustomSwipeViewRenderer))]

since you can apply the render to SwipeView and there is no need to create a new control.
It's cleaner this way, and there is no need to recompile Xamarin.Forms.Platforms.Android.

Regarding SwipeView in iOS, unfortunately things are much worse
I'll have to agree with others that SwipeView is not production ready for iOS. In my case I faced way too many issues...
Just to name a few:

  1. The scroll on the parent component remains disabled in several cases
  2. The contents of the SwipeView items are getting rendered in the wrong place (offset according to the swipe direction)
  3. Swipe will not always disable the parent scroll
  4. For both iOS and Android, swipe will stop if your finder jumps to the next item in the list. This is not the case for native swipe items (check how swipe behaves in iMessages for example)

I really hope all those issues are resolved with MAUI...
I've lost my hope with Xamarin, since most issues are open for quite some time now.

@GivennameSurname
Copy link

Very good @Zwawoo! I've tested this approach and it works well...
I've made a minor change...
[assembly: ExportRenderer(typeof(SwipeView), typeof(App.Droid.CustomRenderer.DroidCustomSwipeViewRenderer))]

since you can apply the render to SwipeView and there is no need to create a new control.
It's cleaner this way, and there is no need to recompile Xamarin.Forms.Platforms.Android.

Regarding SwipeView in iOS, unfortunately things are much worse
I'll have to agree with others that SwipeView is not production ready for iOS. In my case I faced way too many issues...
Just to name a few:

  1. The scroll on the parent component remains disabled in several cases
  2. The contents of the SwipeView items are getting rendered in the wrong place (offset according to the swipe direction)
  3. Swipe will not always disable the parent scroll
  4. For both iOS and Android, swipe will stop if your finder jumps to the next item in the list. This is not the case for native swipe items (check how swipe behaves in iMessages for example)

I really hope all those issues are resolved with MAUI...
I've lost my hope with Xamarin, since most issues are open for quite some time now.

Besides your points (which I can confirm), I´ll add another Point:

5.1 If you try to disable swipe while scrolling in a listview it will start flickering or
5.2 it simple disable it all the time

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
a/collectionview a/swipeview in-progress This issue has an associated pull request that may resolve it! p/iOS 🍎 t/bug 🐛
Projects
None yet
Development

Successfully merging a pull request may close this issue.