Отслеживание нескольких сенсорных пальцев в Xamarin.iOS

В этом документе показано, как отслеживать события касания с нескольких пальцев

Иногда требуется отслеживать отдельные пальцы при одновременном перемещении на экране. Одно из типичных приложений — это программа с пальцем. Вы хотите, чтобы пользователь мог рисовать с одним пальцем, а также рисовать с несколькими пальцами одновременно. При обработке нескольких событий касания программа должна различать эти пальцы.

Когда пальцем сначала прикасается к экрану, iOS создает UITouch объект для этого пальца. Этот объект остается таким же, как пальцем перемещается на экране, а затем поднимается с экрана, в какой момент объект удаляется. Чтобы отслеживать пальцы, программа должна избегать хранения этого UITouch объекта напрямую. Вместо этого он может использовать Handle свойство типа IntPtr для уникальной идентификации этих UITouch объектов.

Почти всегда программа, которая отслеживает отдельные пальцы, поддерживает словарь для отслеживания сенсорного ввода. Для программы iOS ключ словаря — это Handle значение, определяющее определенный палец. Значение словаря зависит от приложения. В программе FingerPaint каждый штрих пальца (от касания к выпуску) связан с объектом, который содержит все сведения, необходимые для отрисовки линии, нарисованной с помощью этого пальца. Программа определяет небольшой FingerPaintPolyline класс для этой цели:

class FingerPaintPolyline
{
    public FingerPaintPolyline()
    {
        Path = new CGPath();
    }

    public CGColor Color { set; get; }

    public float StrokeWidth { set; get; }

    public CGPath Path { private set; get; }
}

Каждая полилайн имеет цвет, ширину штриха и графический CGPath объект iOS для накапливания и отрисовки нескольких точек линии по мере рисования.

Весь остальной код, показанный ниже, содержится в производном UIView именованном FingerPaintCanvasViewкоде. Этот класс поддерживает словарь объектов типа FingerPaintPolyline во время их активного рисования одним или несколькими пальцами:

Dictionary<IntPtr, FingerPaintPolyline> inProgressPolylines = new Dictionary<IntPtr, FingerPaintPolyline>();

Этот словарь позволяет представлению быстро получать FingerPaintPolyline сведения, связанные с каждым пальцем на Handle основе свойства UITouch объекта.

Класс FingerPaintCanvasView также поддерживает List объект для завершенных полилайнов:

List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();

Объекты в этом List порядке находятся в том же порядке, что и они были нарисованы.

FingerPaintCanvasView переопределяет пять методов, определенных в следующих Viewметодах:

Touches Различные переопределения накапливают точки, составляющие полилайны.

Переопределение [Draw] рисует завершенные полилайны, а затем выполняется полилайн:

public override void Draw(CGRect rect)
{
    base.Draw(rect);

    using (CGContext context = UIGraphics.GetCurrentContext())
    {
        // Stroke settings
        context.SetLineCap(CGLineCap.Round);
        context.SetLineJoin(CGLineJoin.Round);

        // Draw the completed polylines
        foreach (FingerPaintPolyline polyline in completedPolylines)
        {
            context.SetStrokeColor(polyline.Color);
            context.SetLineWidth(polyline.StrokeWidth);
            context.AddPath(polyline.Path);
            context.DrawPath(CGPathDrawingMode.Stroke);
        }

        // Draw the in-progress polylines
        foreach (FingerPaintPolyline polyline in inProgressPolylines.Values)
        {
            context.SetStrokeColor(polyline.Color);
            context.SetLineWidth(polyline.StrokeWidth);
            context.AddPath(polyline.Path);
            context.DrawPath(CGPathDrawingMode.Stroke);
        }
    }
}

Каждая из Touches переопределения потенциально сообщает действия нескольких пальцев, указанные одним или несколькими UITouch объектами, хранящимися в аргументе touches в методе. Переопределяет TouchesBegan цикл по этим объектам. Для каждого UITouch объекта метод создает и инициализирует новый FingerPaintPolyline объект, включая хранение исходного расположения пальца, полученного из LocationInView метода. Этот FingerPaintPolyline объект добавляется в InProgressPolylines словарь с помощью Handle свойства UITouch объекта в качестве ключа словаря:

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

    foreach (UITouch touch in touches.Cast<UITouch>())
    {
        // Create a FingerPaintPolyline, set the initial point, and store it
        FingerPaintPolyline polyline = new FingerPaintPolyline
        {
            Color = StrokeColor,
            StrokeWidth = StrokeWidth,
        };

        polyline.Path.MoveToPoint(touch.LocationInView(this));
        inProgressPolylines.Add(touch.Handle, polyline);
    }
    SetNeedsDisplay();
}

Метод завершается вызовом SetNeedsDisplay для создания вызова Draw переопределения и обновления экрана.

При переопределении пальца или пальцев на экране View получает несколько вызовов TouchesMoved . Это переопределяет аналогичные циклы по UITouch объектам, хранящимся в аргументе touches , и добавляет текущее расположение пальца в графический путь:

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

    foreach (UITouch touch in touches.Cast<UITouch>())
    {
        // Add point to path
        inProgressPolylines[touch.Handle].Path.AddLineToPoint(touch.LocationInView(this));
    }
    SetNeedsDisplay();
}

Коллекция touches содержит только те UITouch объекты, которые перемещаются с момента последнего вызова TouchesBegan или TouchesMoved. Если вам когда-либо нужны UITouch объекты, соответствующие всем пальцам в настоящее время в контакте с экраном, эта информация доступна через AllTouches свойство UIEvent аргумента в методе.

Переопределение TouchesEnded имеет два задания. Он должен добавить последнюю точку в графический путь и передать FingerPaintPolyline объект из inProgressPolylines словаря в completedPolylines список:

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

    foreach (UITouch touch in touches.Cast<UITouch>())
    {
        // Get polyline from dictionary and remove it from dictionary
        FingerPaintPolyline polyline = inProgressPolylines[touch.Handle];
        inProgressPolylines.Remove(touch.Handle);

        // Add final point to path and save with completed polylines
        polyline.Path.AddLineToPoint(touch.LocationInView(this));
        completedPolylines.Add(polyline);
    }
    SetNeedsDisplay();
}

TouchesCancelled Переопределение обрабатывается путем простого отказа FingerPaintPolyline от объекта в словаре:

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

    foreach (UITouch touch in touches.Cast<UITouch>())
    {
        inProgressPolylines.Remove(touch.Handle);
    }
    SetNeedsDisplay();
}

В целом эта обработка позволяет программе FingerPaint отслеживать отдельные пальцы и рисовать результаты на экране:

Tracking individual fingers and drawing the results on the screen

Теперь вы узнали, как можно отслеживать отдельные пальцы на экране и различать их.