チュートリアル: Xamarin.iOS でのタッチの使用

このチュートリアルでは、さまざまな種類のタッチ イベントに応答するコードの書き方を示します。 それぞれの例は、個別の画面に含まれています。

各セクションには、ゼロからコードを書くための手順が含まれています。

以下の手順に従ってストーリーボードにコードを追加し、iOS で使用できるさまざまな種類のタッチ イベントについて学習します。

タッチ サンプル

このサンプルでは、タッチ API の一部について説明します。 タッチ イベントを実装するために必要なコードを追加するには、次の手順に従います。

  1. プロジェクト Touch_Startを開きます。 まず、プロジェクトを実行してすべてが問題ないことを確認し、[Touch Samples]\(タッチ サンプル\) ボタンをタッチします。 次のような画面が表示されます (ただし、どのボタンも機能しません)。

    動作しないボタンを使用したサンプル アプリの実行

  2. ファイル TouchViewController.cs を編集し、次の 2 つのインスタンス変数をクラス TouchViewController に追加します。

    #region Private Variables
    private bool imageHighlighted = false;
    private bool touchStartedInside;
    #endregion
    
  3. 次のコードに示すように、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 が移動します。

  4. ユーザーが指を画面から離したとき、または iOS がタッチ イベントを取り消した場合に処理する必要があります。 このためには、以下に示すように TouchesEndedTouchesCancelled を実装します。

    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 も表示されます。

  5. この時点で、タッチ サンプル画面が終了します。 次のスクリーンショットに示すように、各イメージを操作すると画面がどのように変化するかに注目してください。

    アプリの開始画面

    ユーザーがボタンをドラッグした後の画面

ジェスチャ認識エンジンのサンプル

前のセクションでは、タッチ イベントを使用して画面の周りにオブジェクトをドラッグする方法について説明しました。 このセクションでは、タッチ イベントを取り除き、次のジェスチャ認識エンジンを使用する方法について説明します。

  • イメージをドラッグして移動するための UIPanGestureRecognizer
  • 画面のダブル タップに反応する UITapGestureRecognizer

ジェスチャ認識エンジンを実装するには、次の手順に従います。

  1. ファイル GestureViewController.cs を編集し、次のインスタンス変数を追加します。

    #region Private Variables
    private bool imageHighlighted = false;
    private RectangleF originalImageFrame = RectangleF.Empty;
    #endregion
    

    イメージの以前の場所を追跡するには、このインスタンス変数が必要です。 パン ジェスチャ認識エンジンは、値 originalImageFrame を使用して、画面上でイメージを再描画するために必要なオフセットを計算します。

  2. 次のメソッドをコントローラーに追加します。

    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 の形式でジェスチャにターゲットを割り当てることに注意してください。このメソッドは次の手順で提供されます。

  3. 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 つのイメージのドラッグをサポートできるようになりました。

  4. 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 を使用しています。

  5. 最後に、追加したメソッドが呼び出されるように ViewDidLoad を変更する必要があります。 ViewDidLoad を変更すると、次のコードのようになります。

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
    
        Title = "Gesture Recognizers";
    
        // Save initial state
        originalImageFrame = DragImage.Frame;
    
        WireUpTapGestureRecognizer();
        WireUpDragGestureRecognizer();
    }
    

    originalImageFrame の値を初期化していることにも注目してください。

  6. アプリケーションを実行し、2 つのイメージを操作します。 次のスクリーンショットは、これらの操作の一例を示しています。

    このスクリーンショットは、ドラッグ操作を示しています

カスタム ジェスチャ認識エンジン

このセクションでは、前のセクションの概念を適用して、カスタム ジェスチャ認識エンジンをビルドします。 カスタム ジェスチャ認識エンジンは UIGestureRecognizer をサブクラス化し、ユーザーが画面上に "V" を描画した後、ビットマップを切り替えるタイミングを認識します。 次のスクリーンショットは、この画面の例です。

ユーザーが画面に V を描画すると、アプリで認識されます

カスタム ジェスチャ認識エンジンを作成するには、次の手順に従います。

  1. プロジェクトに 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 に変更された場合に呼び出されます。 これは、カスタム ジェスチャ認識エンジンで設定された内部状態をリセットする時間です。 これで、ユーザーが次にアプリケーションを操作したときにクラスが新たに開始され、ジェスチャの認識を再試行する準備が整います。

  2. これで、カスタム ジェスチャ認識エンジン (CheckmarkGestureRecognizer) を定義したので、CustomGestureViewController.cs ファイルを編集し、次の 2 つのインスタンス変数を追加します。

    #region Private Variables
    private bool isChecked = false;
    private CheckmarkGestureRecognizer checkmarkGesture;
    #endregion
    
  3. ジェスチャ認識エンジンをインスタンス化して構成するには、コントローラーに次のメソッドを追加します。

    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);
    }
    
  4. 次のコード スニペットに示すように、WireUpCheckmarkGestureRecognizer が呼び出されるように ViewDidLoad を編集します。

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
    
        // Wire up the gesture recognizer
        WireUpCheckmarkGestureRecognizer();
    }
    
  5. アプリケーションを実行し、画面に "V" を描画してみてください。 次のスクリーンショットに示すように、変更が表示されているイメージが表示されます。

    オンになっているボタン

    オフになっているボタン

上記の 3 つのセクションでは、iOS のタッチ イベントに応答するさまざまな方法を示しました。タッチ イベント、組み込みのジェスチャ認識エンジン、またはカスタム ジェスチャ認識エンジンを使用します。