Rilevamento con più dita a tocco in Xamarin.iOS

Questo documento illustra come tenere traccia degli eventi di tocco da più dita

Ci sono momenti in cui un'applicazione multitocco deve tenere traccia delle singole dita mentre si spostano contemporaneamente sullo schermo. Un'applicazione tipica è un programma di pittura di dito. Si vuole che l'utente sia in grado di disegnare con un solo dito, ma anche di disegnare con più dita contemporaneamente. Poiché il programma elabora più eventi di tocco, deve distinguere tra queste dita.

Quando un dito tocca per la prima volta lo schermo, iOS crea un UITouch oggetto per quel dito. Questo oggetto rimane lo stesso del dito che si muove sullo schermo e quindi solleva dallo schermo, a quel punto l'oggetto viene eliminato. Per tenere traccia delle dita, un programma deve evitare di archiviare direttamente questo UITouch oggetto. Può invece usare la Handle proprietà di tipo IntPtr per identificare in modo univoco questi UITouch oggetti.

Quasi sempre, un programma che tiene traccia delle singole dita mantiene un dizionario per il rilevamento del tocco. Per un programma iOS, la chiave del dizionario è il Handle valore che identifica un dito specifico. Il valore del dizionario dipende dall'applicazione. Nel programma di esempio, ogni tratto del dito (dal tocco al rilascio) è associato a un oggetto che contiene tutte le informazioni necessarie per eseguire il rendering della linea disegnata con il dito. Il programma definisce una piccola FingerPaintPolyline classe a questo scopo:

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

    public CGColor Color { set; get; }

    public float StrokeWidth { set; get; }

    public CGPath Path { private set; get; }
}

Ogni polilinea ha un colore, una larghezza del tratto e un oggetto grafico CGPath iOS per accumulare ed eseguire il rendering di più punti della linea durante il disegno.

Tutto il resto del codice illustrato di seguito è contenuto in un UIView derivato denominato FingerPaintCanvasView. Tale classe mantiene un dizionario di oggetti di tipo FingerPaintPolyline durante il tempo in cui vengono disegnati attivamente da una o più dita:

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

Questo dizionario consente alla visualizzazione di ottenere rapidamente le FingerPaintPolyline informazioni associate a ogni dito in base alla Handle proprietà dell'oggetto UITouch .

La FingerPaintCanvasView classe gestisce anche un List oggetto per le polilinee completate:

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

Gli oggetti in questo List oggetto si trovano nello stesso ordine in cui sono stati disegnati.

FingerPaintCanvasView esegue l'override di cinque metodi definiti da View:

Le varie Touches sostituzioni accumulano i punti che costituiscono le polilinee.

L'override [Draw] disegna le polilinee completate e quindi le polilinee in corso:

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

Ognuna delle Touches sostituzioni segnala potenzialmente le azioni di più dita, indicate da uno o più UITouch oggetti archiviati nell'argomento touches al metodo . Le TouchesBegan sostituzioni eseguono il ciclo di questi oggetti. Per ogni UITouch oggetto, il metodo crea e inizializza un nuovo FingerPaintPolyline oggetto, inclusa l'archiviazione della posizione iniziale del dito ottenuto dal LocationInView metodo . Questo FingerPaintPolyline oggetto viene aggiunto al InProgressPolylines dizionario usando la Handle proprietà dell'oggetto UITouch come chiave del dizionario:

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

Il metodo termina chiamando SetNeedsDisplay per generare una chiamata all'override Draw e per aggiornare la schermata.

Quando il dito o le dita si spostano sullo schermo, ottiene View più chiamate al suo TouchesMoved override. Questo override esegue un ciclo simile negli UITouch oggetti archiviati nell'argomento touches e aggiunge la posizione corrente del dito al percorso grafico:

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

L'insieme touches contiene solo gli UITouch oggetti per le dita spostate dall'ultima chiamata a TouchesBegan o TouchesMoved. Se sono necessari UITouch oggetti corrispondenti a tutte le dita attualmente in contatto con lo schermo, tali informazioni sono disponibili tramite la AllTouches proprietà dell'argomento UIEvent al metodo .

L'override TouchesEnded ha due processi. Deve aggiungere l'ultimo punto al percorso grafico e trasferire l'oggetto FingerPaintPolyline dal inProgressPolylines dizionario all'elenco 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();
}

L'override TouchesCancelled viene gestito semplicemente abbandonando l'oggetto FingerPaintPolyline nel dizionario:

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

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

Complessivamente, questa elaborazione consente al programma di esempio di tenere traccia delle singole dita e disegnare i risultati sullo schermo:

Rilevamento delle singole dita e disegno dei risultati sullo schermo

Ora hai visto come tenere traccia delle singole dita sullo schermo e distinguerle.