Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zoom is not working on iOS #10

Closed
RobertStefancic opened this issue May 31, 2024 · 12 comments
Closed

Zoom is not working on iOS #10

RobertStefancic opened this issue May 31, 2024 · 12 comments
Labels

Comments

@RobertStefancic
Copy link
Contributor

The TouchRecognizer is not receiving all touch input events, which results in slow and snappy behavior when trying to pan/zoom on the plot.

@jaisont07
Copy link

jaisont07 commented Jun 16, 2024

Does the sample here has a plot where zoom is supported in iOS? @janusw
Because, the zoom doesn't seems to be working for pinch to zoom. Instead its panning the plot.

@janusw janusw added the OS:iOS label Jul 13, 2024
@janusw
Copy link
Member

janusw commented Jul 14, 2024

Does the sample here has a plot where zoom is supported in iOS? @janusw

Actually most of the samples in the ExampleBrowser should support zoom, e.g. the first one in the list (Annotations -> Tool tips). Panning and zooming that example works well for me on Android.

Because, the zoom doesn't seems to be working for pinch to zoom. Instead its panning the plot.

Yes, I can confirm that zooming via two-finger pinch gesture does not work at all on iOS. Single-finger panning seems to work well, though.

What @RobertStefancic means by "snappy behavior" is probably that the plot sometimes jumps around wildly when using two-finger pinch.

@janusw janusw changed the title Pan and zoom is slow and snappy on iOS Zoom is now working on iOS Jul 14, 2024
@janusw janusw changed the title Zoom is now working on iOS Zoom is not working on iOS Jul 14, 2024
@janusw
Copy link
Member

janusw commented Jul 14, 2024

AFAICS the problem is that iOS always generates TouchAction events with a single point only ...

onTouchAction(recognizer.element,
new TouchActionEventArgs(id, actionType, new[] { xfPoint }, isInContact));

... while Android generates touch events with multiple points:

onTouchAction(platformTouchEffect._formsElement,
new TouchActionEventArgs(id, actionType, locations.ToArray(), isInContact));

@janusw
Copy link
Member

janusw commented Jul 14, 2024

I took a first stab at fixing it on this branch:

https://github.com/janusw/oxyplot-maui/tree/iOS_fix_zoom

It basically makes zooming work in iOS, but in some cases it seems to behave strangely. I'll need to take another look at it soon ...

@jaisont07
Copy link

@janusw, Actually I have made a fix for this which is working fine, If possible to give me the permission, I can raise a PR for the same.

@janusw
Copy link
Member

janusw commented Jul 15, 2024

You should be able to create a PR: Just fork the repo, make a new branch, commit your changes, push them, and open a PR for that branch.

@RobertStefancic
Copy link
Contributor Author

RobertStefancic commented Jul 15, 2024

Hello @janusw and @jaisont07.
Thank you for taking the time to look into the issue.

I have also developed a solution for the issue that has been accepted and already used in production. Unfortunately I have not been able to create a PR yet due to some issues with GitHub here at work. I planned to do it from my personal account at home, but didn't have the opportunity just yet.

However, I will paste the code here until then. If you find that it's working for you as well, I don't mind you integrating it.

using CoreGraphics;
using Foundation;
using Microsoft.Maui.Controls.Platform;
using OxyPlot.Maui.Skia.Effects;
using UIKit;

namespace OxyPlot.Maui.Skia.ios.Effects;

public class PlatformTouchEffect : PlatformEffect
{
    private UIView view;
    private TouchRecognizer touchRecognizer;

    protected override void OnAttached()
    {
        view = Control ?? Container;

        var touchEffect = Element.Effects.OfType<MyTouchEffect>().FirstOrDefault();

        if (touchEffect != null && view != null)
        {
            touchRecognizer = new TouchRecognizer(Element, touchEffect);
            view.AddGestureRecognizer(touchRecognizer);
        }
    }

    protected override void OnDetached()
    {
        if (touchRecognizer != null)
        {
            touchRecognizer.Detach();
            view.RemoveGestureRecognizer(touchRecognizer);
        }
    }
}

internal class TouchRecognizer : UIGestureRecognizer
{
    private readonly Microsoft.Maui.Controls.Element element;
    private readonly MyTouchEffect touchEffect;
    private uint activeTouchesCount = 0;

    public TouchRecognizer(Microsoft.Maui.Controls.Element element, MyTouchEffect touchEffect)
    {
        this.element = element;
        this.touchEffect = touchEffect;

        ShouldRecognizeSimultaneously = new UIGesturesProbe((_, _) => true);
    }

    public void Detach()
    {
        ShouldRecognizeSimultaneously = null;
    }

    public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);
        activeTouchesCount += touches.Count.ToUInt32();
        FireEvent(touches, TouchActionType.Pressed, true);
    }

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

        if (activeTouchesCount == touches.Count.ToUInt32())
        {
            FireEvent(touches, TouchActionType.Moved, true);
        }
    }

    public override void TouchesEnded(NSSet touches, UIEvent evt)
    {
        base.TouchesEnded(touches, evt);
        activeTouchesCount -= touches.Count.ToUInt32();
        FireEvent(touches, TouchActionType.Released, false);
    }

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

    private void FireEvent(NSSet touches, TouchActionType actionType, bool isInContact)
    {
        UITouch[] uiTouches = touches.Cast<UITouch>().ToArray();
        long id = ((IntPtr)uiTouches.First().Handle).ToInt64();
        Point[] points = new Point[uiTouches.Length];

        for (int i = 0; i < uiTouches.Length; i++)
        {
            CGPoint cgPoint = uiTouches[i].LocationInView(View);
            points[i] = new(cgPoint.X, cgPoint.Y);
        }
        touchEffect.OnTouchAction(element, new(id, actionType, points, isInContact));
    }
}

@jaisont07
Copy link

You should be able to create a PR: Just fork the repo, make a new branch, commit your changes, push them, and open a PR for that branch.

Raised a PR

@janusw
Copy link
Member

janusw commented Jul 17, 2024

Raised a PR

Awesome, thanks. I'm currently traveling and won't be able to review it before next week.

In the meantime, could you please check the differences to my branch mentioned above:

https://github.com/janusw/oxyplot-maui/tree/iOS_fix_zoom

What are you doing differently? And why do you do it like that?

@janusw
Copy link
Member

janusw commented Jul 17, 2024

I have also developed a solution for the issue that has been accepted and already used in production. Unfortunately I have not been able to create a PR yet due to some issues with GitHub here at work. I planned to do it from my personal account at home, but didn't have the opportunity just yet.

However, I will paste the code here until then. If you find that it's working for you as well, I don't mind you integrating it.

If you want this to be considered for inclusion in the repo, please push it to a branch on your fork (and open a PR).

Seems like we have three fixes by now that we can cross-check against each other 😊

@janusw
Copy link
Member

janusw commented Sep 19, 2024

This issue is fixed via #15.

@janusw janusw closed this as completed Sep 19, 2024
@janusw
Copy link
Member

janusw commented Sep 19, 2024

The fix is available in version 1.0.1, which was just released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants