チュートリアル: Xamarin.iOS でのタッチの使用
このチュートリアルでは、さまざまな種類のタッチ イベントに応答するコードの書き方を示します。 それぞれの例は、個別の画面に含まれています。
- タッチ サンプル – タッチ イベントに応答する方法。
- ジェスチャ認識エンジンのサンプル – 組み込みのジェスチャ認識エンジンを使用する方法。
- カスタム ジェスチャ認識エンジンのサンプル – カスタム ジェスチャ認識エンジンを構築する方法。
各セクションには、ゼロからコードを書くための手順が含まれています。
以下の手順に従ってストーリーボードにコードを追加し、iOS で使用できるさまざまな種類のタッチ イベントについて学習します。
タッチ サンプル
このサンプルでは、タッチ API の一部について説明します。 タッチ イベントを実装するために必要なコードを追加するには、次の手順に従います。
プロジェクト Touch_Startを開きます。 まず、プロジェクトを実行してすべてが問題ないことを確認し、[Touch Samples]\(タッチ サンプル\) ボタンをタッチします。 次のような画面が表示されます (ただし、どのボタンも機能しません)。
ファイル TouchViewController.cs を編集し、次の 2 つのインスタンス変数をクラス
TouchViewController
に追加します。#region Private Variables private bool imageHighlighted = false; private bool touchStartedInside; #endregion
次のコードに示すように、
TouchesBegan
メソッドを実装します。public override void TouchesBegan(NSSet touches, UIEvent evt) { base.TouchesBegan(touches, evt); // If Multitouch is enabled, report the number of fingers down TouchStatus.Text = string.Format ("Number of fingers {0}", touches.Count); // Get the current touch UITouch touch = touches.AnyObject as UITouch; if (touch != null) { // Check to see if any of the images have been touched if (TouchImage.Frame.Contains(touch.LocationInView(TouchView))) { // Fist image touched TouchImage.Image = UIImage.FromBundle("TouchMe_Touched.png"); TouchStatus.Text = "Touches Began"; } else if (touch.TapCount == 2 && DoubleTouchImage.Frame.Contains(touch.LocationInView(TouchView))) { // Second image double-tapped, toggle bitmap if (imageHighlighted) { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe.png"); TouchStatus.Text = "Double-Tapped Off"; } else { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe_Highlighted.png"); TouchStatus.Text = "Double-Tapped On"; } imageHighlighted = !imageHighlighted; } else if (DragImage.Frame.Contains(touch.LocationInView(View))) { // Third image touched, prepare to drag touchStartedInside = true; } } }
このメソッドは、
UITouch
オブジェクトをチェックすることで機能し、存在する場合はタッチが発生した場所に基づいて何らかのアクションを実行します。- 内部 TouchImage - ラベルにテキスト
Touches Began
を表示し、イメージを変更します。 - 内部 DoubleTouchImage – ジェスチャがダブルタップされた場合に表示されるイメージを変更します。
- 内部 DragImage - タッチが開始されたことを示すフラグを設定します。 メソッド
TouchesMoved
は、次の手順で示すように、このフラグを使用して、DragImage
を画面内で移動させる必要があるかどうかを判断します。
上記のコードは個々のタッチのみを処理しますが、ユーザーが画面上で指を動かしている場合はまだ動作がありません。 移動に対応するには、次のコードに示すように
TouchesMoved
を実装します。public override void TouchesMoved(NSSet touches, UIEvent evt) { base.TouchesMoved(touches, evt); // get the touch UITouch touch = touches.AnyObject as UITouch; if (touch != null) { //==== IMAGE TOUCH if (TouchImage.Frame.Contains(touch.LocationInView(TouchView))) { TouchStatus.Text = "Touches Moved"; } //==== IMAGE DRAG // check to see if the touch started in the drag me image if (touchStartedInside) { // move the shape float offsetX = touch.PreviousLocationInView(View).X - touch.LocationInView(View).X; float offsetY = touch.PreviousLocationInView(View).Y - touch.LocationInView(View).Y; DragImage.Frame = new RectangleF(new PointF(DragImage.Frame.X - offsetX, DragImage.Frame.Y - offsetY), DragImage.Frame.Size); } } }
このメソッドで
UITouch
オブジェクトを取得し、チェックしてタッチが発生した場所を確認します。TouchImage
でタッチが発生した場合は、[タッチが移動しました] というテキストが画面に表示されます。touchStartedInside
が true の場合、ユーザーがDragImage
上に指を置いて動かしていることがわかります。 ユーザーが画面上で指を動かすと、コードでDragImage
が移動します。- 内部 TouchImage - ラベルにテキスト
ユーザーが指を画面から離したとき、または iOS がタッチ イベントを取り消した場合に処理する必要があります。 このためには、以下に示すように
TouchesEnded
とTouchesCancelled
を実装します。public override void TouchesCancelled(NSSet touches, UIEvent evt) { base.TouchesCancelled(touches, evt); // reset our tracking flags touchStartedInside = false; TouchImage.Image = UIImage.FromBundle("TouchMe.png"); TouchStatus.Text = ""; } public override void TouchesEnded(NSSet touches, UIEvent evt) { base.TouchesEnded(touches, evt); // get the touch UITouch touch = touches.AnyObject as UITouch; if (touch != null) { //==== IMAGE TOUCH if (TouchImage.Frame.Contains(touch.LocationInView(TouchView))) { TouchImage.Image = UIImage.FromBundle("TouchMe.png"); TouchStatus.Text = "Touches Ended"; } } // reset our tracking flags touchStartedInside = false; }
どちらの方法でも、
touchStartedInside
フラグがfalse にリセットされます。TouchesEnded
により、画面上にTouchesEnded
も表示されます。この時点で、タッチ サンプル画面が終了します。 次のスクリーンショットに示すように、各イメージを操作すると画面がどのように変化するかに注目してください。
ジェスチャ認識エンジンのサンプル
前のセクションでは、タッチ イベントを使用して画面の周りにオブジェクトをドラッグする方法について説明しました。 このセクションでは、タッチ イベントを取り除き、次のジェスチャ認識エンジンを使用する方法について説明します。
- イメージをドラッグして移動するための
UIPanGestureRecognizer
。 - 画面のダブル タップに反応する
UITapGestureRecognizer
。
ジェスチャ認識エンジンを実装するには、次の手順に従います。
ファイル GestureViewController.cs を編集し、次のインスタンス変数を追加します。
#region Private Variables private bool imageHighlighted = false; private RectangleF originalImageFrame = RectangleF.Empty; #endregion
イメージの以前の場所を追跡するには、このインスタンス変数が必要です。 パン ジェスチャ認識エンジンは、値
originalImageFrame
を使用して、画面上でイメージを再描画するために必要なオフセットを計算します。次のメソッドをコントローラーに追加します。
private void WireUpDragGestureRecognizer() { // Create a new tap gesture UIPanGestureRecognizer gesture = new UIPanGestureRecognizer(); // Wire up the event handler (have to use a selector) gesture.AddTarget(() => HandleDrag(gesture)); // to be defined // Add the gesture recognizer to the view DragImage.AddGestureRecognizer(gesture); }
このコードは
UIPanGestureRecognizer
インスタンスをインスタンス化し、ビューに追加します。 メソッドHandleDrag
の形式でジェスチャにターゲットを割り当てることに注意してください。このメソッドは次の手順で提供されます。HandleDrag を実装するには、次のコードをコントローラーに追加します。
private void HandleDrag(UIPanGestureRecognizer recognizer) { // If it's just began, cache the location of the image if (recognizer.State == UIGestureRecognizerState.Began) { originalImageFrame = DragImage.Frame; } // Move the image if the gesture is valid if (recognizer.State != (UIGestureRecognizerState.Cancelled | UIGestureRecognizerState.Failed | UIGestureRecognizerState.Possible)) { // Move the image by adding the offset to the object's frame PointF offset = recognizer.TranslationInView(DragImage); RectangleF newFrame = originalImageFrame; newFrame.Offset(offset.X, offset.Y); DragImage.Frame = newFrame; } }
上記のコードは、まずジェスチャ認識エンジンの状態をチェックしてから、画面の周りでイメージを移動させます。 このコードを配置すると、コントローラーは画面の周りの 1 つのイメージのドラッグをサポートできるようになりました。
DoubleTouchImage に表示されるイメージを変更する
UITapGestureRecognizer
を追加します。 次のメソッドをGestureViewController
コントローラーに追加します。private void WireUpTapGestureRecognizer() { // Create a new tap gesture UITapGestureRecognizer tapGesture = null; // Report touch Action action = () => { TouchStatus.Text = string.Format("Image touched at: {0}",tapGesture.LocationOfTouch(0, DoubleTouchImage)); // Toggle the image if (imageHighlighted) { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe.png"); } else { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe_Highlighted.png"); } imageHighlighted = !imageHighlighted; }; tapGesture = new UITapGestureRecognizer(action); // Configure it tapGesture.NumberOfTapsRequired = 2; // Add the gesture recognizer to the view DoubleTouchImage.AddGestureRecognizer(tapGesture); }
このコードは
UIPanGestureRecognizer
のコードとよく似ていますが、ターゲットにデリゲートを使用する代わりにAction
を使用しています。最後に、追加したメソッドが呼び出されるように
ViewDidLoad
を変更する必要があります。 ViewDidLoad を変更すると、次のコードのようになります。public override void ViewDidLoad() { base.ViewDidLoad(); Title = "Gesture Recognizers"; // Save initial state originalImageFrame = DragImage.Frame; WireUpTapGestureRecognizer(); WireUpDragGestureRecognizer(); }
originalImageFrame
の値を初期化していることにも注目してください。アプリケーションを実行し、2 つのイメージを操作します。 次のスクリーンショットは、これらの操作の一例を示しています。
カスタム ジェスチャ認識エンジン
このセクションでは、前のセクションの概念を適用して、カスタム ジェスチャ認識エンジンをビルドします。 カスタム ジェスチャ認識エンジンは UIGestureRecognizer
をサブクラス化し、ユーザーが画面上に "V" を描画した後、ビットマップを切り替えるタイミングを認識します。 次のスクリーンショットは、この画面の例です。
カスタム ジェスチャ認識エンジンを作成するには、次の手順に従います。
プロジェクトに
CheckmarkGestureRecognizer
という名前の新しいクラスを追加し、次のコードのようにします。using System; using CoreGraphics; using Foundation; using UIKit; namespace Touch { public class CheckmarkGestureRecognizer : UIGestureRecognizer { #region Private Variables private CGPoint midpoint = CGPoint.Empty; private bool strokeUp = false; #endregion #region Override Methods /// <summary> /// Called when the touches end or the recognizer state fails /// </summary> public override void Reset() { base.Reset(); strokeUp = false; midpoint = CGPoint.Empty; } /// <summary> /// Is called when the fingers touch the screen. /// </summary> public override void TouchesBegan(NSSet touches, UIEvent evt) { base.TouchesBegan(touches, evt); // we want one and only one finger if (touches.Count != 1) { base.State = UIGestureRecognizerState.Failed; } Console.WriteLine(base.State.ToString()); } /// <summary> /// Called when the touches are cancelled due to a phone call, etc. /// </summary> public override void TouchesCancelled(NSSet touches, UIEvent evt) { base.TouchesCancelled(touches, evt); // we fail the recognizer so that there isn't unexpected behavior // if the application comes back into view base.State = UIGestureRecognizerState.Failed; } /// <summary> /// Called when the fingers lift off the screen /// </summary> public override void TouchesEnded(NSSet touches, UIEvent evt) { base.TouchesEnded(touches, evt); // if (base.State == UIGestureRecognizerState.Possible && strokeUp) { base.State = UIGestureRecognizerState.Recognized; } Console.WriteLine(base.State.ToString()); } /// <summary> /// Called when the fingers move /// </summary> public override void TouchesMoved(NSSet touches, UIEvent evt) { base.TouchesMoved(touches, evt); // if we haven't already failed if (base.State != UIGestureRecognizerState.Failed) { // get the current and previous touch point CGPoint newPoint = (touches.AnyObject as UITouch).LocationInView(View); CGPoint previousPoint = (touches.AnyObject as UITouch).PreviousLocationInView(View); // if we're not already on the upstroke if (!strokeUp) { // if we're moving down, just continue to set the midpoint at // whatever point we're at. when we start to stroke up, it'll stick // as the last point before we upticked if (newPoint.X >= previousPoint.X && newPoint.Y >= previousPoint.Y) { midpoint = newPoint; } // if we're stroking up (moving right x and up y [y axis is flipped]) else if (newPoint.X >= previousPoint.X && newPoint.Y <= previousPoint.Y) { strokeUp = true; } // otherwise, we fail the recognizer else { base.State = UIGestureRecognizerState.Failed; } } } Console.WriteLine(base.State.ToString()); } #endregion } }
Reset メソッドは
State
プロパティがRecognized
またはEnded
に変更された場合に呼び出されます。 これは、カスタム ジェスチャ認識エンジンで設定された内部状態をリセットする時間です。 これで、ユーザーが次にアプリケーションを操作したときにクラスが新たに開始され、ジェスチャの認識を再試行する準備が整います。これで、カスタム ジェスチャ認識エンジン (
CheckmarkGestureRecognizer
) を定義したので、CustomGestureViewController.cs ファイルを編集し、次の 2 つのインスタンス変数を追加します。#region Private Variables private bool isChecked = false; private CheckmarkGestureRecognizer checkmarkGesture; #endregion
ジェスチャ認識エンジンをインスタンス化して構成するには、コントローラーに次のメソッドを追加します。
private void WireUpCheckmarkGestureRecognizer() { // Create the recognizer checkmarkGesture = new CheckmarkGestureRecognizer(); // Wire up the event handler checkmarkGesture.AddTarget(() => { if (checkmarkGesture.State == (UIGestureRecognizerState.Recognized | UIGestureRecognizerState.Ended)) { if (isChecked) { CheckboxImage.Image = UIImage.FromBundle("CheckBox_Unchecked.png"); } else { CheckboxImage.Image = UIImage.FromBundle("CheckBox_Checked.png"); } isChecked = !isChecked; } }); // Add the gesture recognizer to the view View.AddGestureRecognizer(checkmarkGesture); }
次のコード スニペットに示すように、
WireUpCheckmarkGestureRecognizer
が呼び出されるようにViewDidLoad
を編集します。public override void ViewDidLoad() { base.ViewDidLoad(); // Wire up the gesture recognizer WireUpCheckmarkGestureRecognizer(); }
アプリケーションを実行し、画面に "V" を描画してみてください。 次のスクリーンショットに示すように、変更が表示されているイメージが表示されます。
上記の 3 つのセクションでは、iOS のタッチ イベントに応答するさまざまな方法を示しました。タッチ イベント、組み込みのジェスチャ認識エンジン、またはカスタム ジェスチャ認識エンジンを使用します。