Compartilhar via


Rastreamento de dedos multitoque no Xamarin.iOS

Este documento demonstra como rastrear eventos de toque de vários dedos

Há momentos em que um aplicativo multitoque precisa rastrear dedos individuais enquanto eles se movem simultaneamente na tela. Uma aplicação típica é um programa de pintura a dedo. Você quer que o usuário seja capaz de desenhar com um único dedo, mas também desenhar com vários dedos ao mesmo tempo. Como seu programa processa vários eventos de toque, ele precisa distinguir entre esses dedos.

Quando um dedo toca pela primeira vez na tela, o iOS cria um UITouch objeto para esse dedo. Esse objeto permanece o mesmo que o dedo se move na tela e, em seguida, levanta da tela, momento em que o objeto é descartado. Para manter o controle dos dedos, um programa deve evitar armazenar esse UITouch objeto diretamente. Em vez disso, ele pode usar a Handle propriedade de tipo IntPtr para identificar exclusivamente esses UITouch objetos.

Quase sempre, um programa que rastreia dedos individuais mantém um dicionário para rastreamento por toque. Para um programa iOS, a chave do dicionário é o Handle valor que identifica um dedo específico. O valor do dicionário depende do aplicativo. No programa de exemplo, cada traçado do dedo (do toque à liberação) é associado a um objeto que contém todas as informações necessárias para renderizar a linha desenhada com esse dedo. O programa define uma pequena FingerPaintPolyline classe para este fim:

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

    public CGColor Color { set; get; }

    public float StrokeWidth { set; get; }

    public CGPath Path { private set; get; }
}

Cada polilinha tem uma cor, uma largura de traçado e um objeto gráfico CGPath iOS para acumular e renderizar vários pontos da linha à medida que ela está sendo desenhada.

Todo o resto do código mostrado abaixo está contido em um UIView derivado chamado FingerPaintCanvasView. Essa classe mantém um dicionário de objetos do tipo FingerPaintPolyline durante o tempo em que eles estão sendo ativamente desenhados por um ou mais dedos:

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

Este dicionário permite que a exibição obtenha rapidamente as FingerPaintPolyline informações associadas a cada dedo com base na Handle propriedade do UITouch objeto.

A FingerPaintCanvasView classe também mantém um List objeto para as polilinhas que foram concluídas:

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

Os objetos estão List na mesma ordem em que foram desenhados.

FingerPaintCanvasView substitui cinco métodos definidos por View:

As várias Touches substituições acumulam os pontos que compõem as polilinhas.

A substituição [Draw] desenha as polilinhas concluídas e, em seguida, as polilinhas em andamento:

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);
        }
    }
}

Cada uma das Touches substituições relata potencialmente as ações de vários dedos, indicadas por um ou mais UITouch objetos armazenados no touches argumento para o método. As TouchesBegan substituições percorrem esses objetos. Para cada UITouch objeto, o método cria e inicializa um novo FingerPaintPolyline objeto, incluindo o armazenamento do local inicial do dedo obtido do LocationInView método. Esse FingerPaintPolyline objeto é adicionado ao InProgressPolylines dicionário usando a HandleUITouch propriedade do objeto como uma chave de dicionário:

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();
}

O método é concluído chamando SetNeedsDisplay para gerar uma chamada para a Draw substituição e atualizar a tela.

À medida que o dedo ou os dedos se movem na tela, o View recebe várias chamadas para sua TouchesMoved substituição. Essa substituição também faz um loop pelos UITouch objetos armazenados no touches argumento e adiciona o local atual do dedo ao caminho gráfico:

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();
}

A touches coleção contém apenas os UITouch objetos para os dedos que foram movidos desde a última chamada para TouchesBegan ou TouchesMoved. Se você precisar de UITouch objetos correspondentes a todos os dedos atualmente em contato com a tela, essas informações estarão disponíveis por meio AllTouches da propriedade do UIEvent argumento para o método.

A TouchesEnded substituição tem dois trabalhos. Ele deve adicionar o último ponto ao caminho gráfico e transferir o FingerPaintPolyline objeto do inProgressPolylines dicionário para a completedPolylines lista:

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();
}

A TouchesCancelled substituição é tratada simplesmente abandonando o FingerPaintPolyline objeto no dicionário:

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

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

Em conjunto, esse processamento permite que o programa de amostra rastreie dedos individuais e desenhe os resultados na tela:

Rastreando dedos individuais e desenhando os resultados na tela

Agora você viu como você pode rastrear dedos individuais na tela e distinguir entre eles.