Xamarin.iOS のマルチタッチ フィンガー トラッキング
このドキュメントでは、複数の指によるタッチ イベントを追跡する方法について説明します
マルチタッチ アプリケーションでは、画面上で複数の指が同時に動かされた場合、個々の指を追跡することが必要な場合があります。 一般的なアプリケーションの 1 つとして、フィンガー ペイント プログラムがあります。 あなたは、ユーザーが 1 つの指で描画できるだけでなく、同時に複数の指で描画できるようにする必要があります。 プログラムで複数のタッチ イベントを処理する場合は、これらの指を区別する必要があります。
指が最初に画面に触れると、iOS によってその指の UITouch
オブジェクトが作成されます。 このオブジェクトは画面上を移動する指と同じように保持され、そして指が画面から離れると、その時点でオブジェクトは破棄されます。 指の追跡を続けるために、プログラムではこの UITouch
オブジェクトを直接保存しないようにする必要があります。 代わりに、型 IntPtr
の Handle
プロパティを使用すれば、これらの UITouch
オブジェクトを一意に識別できます。
ほとんどの場合、個々の指を追跡するプログラムには、タッチを追跡するための辞書が用意されています。 iOS プログラムの場合、特定の指を識別する Handle
値が辞書キーとなります。 辞書の値は、アプリケーションによって異なります。 サンプル プログラムでは、各指のストローク (タッチからリリースまで) が、その指で描かれた線をレンダリングするのに必要である情報をすべて含むオブジェクトに関連付けられます。 プログラムでは、これを目的とした小さな FingerPaintPolyline
クラスを定義します。
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new CGPath();
}
public CGColor Color { set; get; }
public float StrokeWidth { set; get; }
public CGPath Path { private set; get; }
}
各ポリラインには、色、ストロークの幅、および描画中に線の複数のポイントを蓄積してレンダリングする iOS グラフィックス CGPath
オブジェクトが設定されます。
以下に示すコードの残りの部分はすべて、FingerPaintCanvasView
という名前の、UIView
の派生クラスに含まれています。 このクラスでは、1 つまたは複数の指によってオブジェクトがアクティブに描画されている間、FingerPaintPolyline
型のオブジェクトの辞書を維持します。
Dictionary<IntPtr, FingerPaintPolyline> inProgressPolylines = new Dictionary<IntPtr, FingerPaintPolyline>();
この辞書を使用すると、各指に関連付けられている FingerPaintPolyline
情報を UITouch
オブジェクトの Handle
プロパティに基づいて、ビューですばやく取得できます。
FingerPaintCanvasView
クラスには、完了したポリラインに対する List
オブジェクトも保持されます。
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
この List
に含まれるオブジェクトは、それらが描画された順番となります。
FingerPaintCanvasView
では、View
によって定義された 5 つのメソッドをオーバーライドします。
各種の 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
オーバーライドからは、メソッドに渡される touches
引数に格納された 1 つまたは複数の UITouch
オブジェクトによって示される、複数の指のアクションが報告される可能性があります。 TouchesBegan
オーバーライドでは、これらのオブジェクトをループ処理します。 各 UITouch
オブジェクトごとに、メソッドでは新しい FingerPaintPolyline
オブジェクトを作成して初期化します。これには、LocationInView
メソッドから取得した指の初期位置を格納することも含まれます。 この FingerPaintPolyline
オブジェクトを InProgressPolylines
辞書に追加するには、UITouch
オブジェクトの Handle
プロパティを辞書キーとして使用します。
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
オーバーライドの呼び出しを生成し、画面を更新することで、メソッドは終了します。
1 つまたは複数の指を画面上で移動させると、View
によって、その TouchesMoved
オーバーライドの複数の呼び出しが行われます。 このオーバーライドでも同様に、touches
引数に格納されている UITouch
オブジェクトをループ処理し、指の現在の位置をグラフィックス パスに追加します。
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
コレクションには、TouchesBegan
または TouchesMoved
の最後の呼び出し以降に移動した指に対応する UITouch
オブジェクトのみが含まれます。 現在画面に触れている "すべて" の指に対応する UITouch
オブジェクトが必要な場合、その情報は、メソッドへの UIEvent
引数の AllTouches
プロパティを介して入手できます。
TouchesEnded
オーバーライドには、2 つのジョブがあります。 グラフィックス パスに最後のポイントを追加し、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();
}
つまり、この処理により、サンプル プログラムでは、個々の指を追跡し、その結果を画面上に描画できるようになります。
これで、画面上の個々の指を追跡し、それらを区別する方法がわかりました。