Tutorial: Uso de la entrada táctil en Xamarin.iOS

En este tutorial se muestra cómo escribir código que responda a diferentes tipos de eventos táctiles. Cada ejemplo se incluye en una pantalla independiente:

Cada sección contiene instrucciones para escribir el código desde cero.

Siga las instrucciones siguientes para agregar código al guión gráfico y obtenga información sobre los diferentes tipos de eventos táctiles disponibles en iOS.

Ejemplos táctiles

En este ejemplo, mostraremos algunas de las API táctiles. Siga estos pasos para agregar el código necesario e implementar eventos táctiles:

  1. Abra el proyectoTouch_Start. En primer lugar, ejecute el proyecto para asegurarse de que todo está bien y toque el botón Ejemplos táctiles. Debería ver una pantalla similar a la siguiente (aunque ninguno de los botones funcionará):

    Ejecución de la aplicación de ejemplo con botones que no funcionan

  2. Edite el archivo TouchViewController.cs y agregue las dos variables de instancia siguientes a la clase TouchViewController:

    #region Private Variables
    private bool imageHighlighted = false;
    private bool touchStartedInside;
    #endregion
    
  3. Implemente el método TouchesBegan como se muestra a continuación:

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

    Este método funciona comprobando si existe un objeto UITouch y, si existe, realiza alguna acción en función de dónde se produjo la función táctil:

    • Dentro de TouchImage: muestra el texto Touches Began en una etiqueta y cambia la imagen.
    • Dentro de DoubleTouchImage: cambia la imagen que se muestra si el gesto fue una pulsación doble.
    • Dentro de DragImage: establece una marca que indica que se ha iniciado la función táctil. El método TouchesMoved usará esta marca para determinar si DragImage se debe mover alrededor de la pantalla o no, como veremos en el paso siguiente.

    El código anterior solo se ocupa de toques individuales, no hay ningún comportamiento si el usuario mueve su dedo en la pantalla. Para responder al movimiento, implemente TouchesMoved como se muestra en el código siguiente:

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

    Este método obtiene un objeto UITouch y, a continuación, comprueba dónde se produjo el contacto. Si el toque se produjo en TouchImage, el texto Touches Moved (Toques movidos) se muestra en la pantalla.

    Si touchStartedInside es cierto, sabemos que el usuario tiene el dedo en DragImage y lo está moviendo. El código se moverá DragImage a medida que el usuario mueve el dedo alrededor de la pantalla.

  4. Necesitamos controlar el caso cuando el usuario levanta su dedo fuera de la pantalla, o iOS cancela el evento táctil. Para ello, implementaremos TouchesEnded y TouchesCancelled como se muestra a continuación:

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

    Ambos métodos restablecerán la marca touchStartedInside a false. TouchesEnded también mostrará TouchesEnded en la pantalla.

  5. En este momento, la pantalla Ejemplos táctiles ha finalizado. Observe cómo cambia la pantalla a medida que interactúa con cada una de las imágenes, como se muestra en la captura de pantalla siguiente:

    Pantalla de la aplicación de inicio

    La pantalla después de que el usuario arrastre un botón

Ejemplos de reconocedor de gestos

En la sección anterior se mostró cómo arrastrar un objeto alrededor de la pantalla mediante eventos táctiles. En esta sección nos desharemos de los eventos táctiles y se mostrará cómo usar los siguientes reconocedores de gestos:

  • El UIPanGestureRecognizer para arrastrar una imagen alrededor de la pantalla.
  • El UITapGestureRecognizer para responder a pulsaciones dobles en la pantalla.

Siga estos pasos para implementar reconocedores de gestos:

  1. Edite el archivo GestureViewController.cs y agregue la siguiente variable de instancia:

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

    Necesitamos esta variable de instancia para realizar un seguimiento de la ubicación anterior de la imagen. El reconocedor de gestos panorámico usará el valor originalImageFrame para calcular el desplazamiento necesario para volver a dibujar la imagen en la pantalla.

  2. Agregue el método siguiente al controlador:

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

    Este código crea una instancia de un UIPanGestureRecognizer y lo agrega a una vista. Observe que asignamos un destino al gesto en forma del método HandleDrag: este método se proporciona en el paso siguiente.

  3. Para implementar HandleDrag, agregue el código siguiente al controlador:

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

    El código anterior comprobará primero el estado del reconocedor de gestos y, a continuación, moverá la imagen alrededor de la pantalla. Con este código en su lugar, el controlador ahora admite que se arrastre una imagen alrededor de la pantalla.

  4. Agregue un UITapGestureRecognizer que cambiará la imagen que se va a mostrar en DoubleTouchImage. Agregue el método siguiente al controlador 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);
    }
    

    Este código es muy similar al código del UIPanGestureRecognizer, pero en lugar de usar un delegado para un destino, estamos usando un Action.

  5. Lo último que debemos hacer es modificar ViewDidLoad para que llame a los métodos que acabamos de agregar. Cambie ViewDidLoad para que se parezca al código siguiente:

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

    Observe también que inicializamos el valor de originalImageFrame.

  6. Ejecute la aplicación e interactúe con las dos imágenes. La siguiente captura de pantalla muestra un ejemplo de estas interacciones:

    En esta captura de pantalla se muestra una interacción de arrastre

Reconocedor de gestos personalizados

En esta sección se aplicarán los conceptos de las secciones anteriores para crear un reconocedor de gestos personalizados. El reconocedor de gestos personalizados reconocerá las subclases UIGestureRecognizer y reconocerá cuando el usuario dibuje una "V" en la pantalla y, a continuación, alternará un mapa de bits. La captura de pantalla siguiente es un ejemplo de esta pantalla:

La aplicación reconocerá cuándo el usuario dibuja una V en la pantalla

Siga estos pasos para crear un reconocedor de gestos personalizados:

  1. Agregue una nueva clase al proyecto denominado CheckmarkGestureRecognizer y haga que tenga un aspecto similar al siguiente:

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

    Se llama al método Reset cuando la propiedad State cambia a Recognized o Ended. Este es el momento de restablecer cualquier estado interno establecido en el reconocedor de gestos personalizados. Ahora la clase puede iniciarse de nuevo la próxima vez que el usuario interactúe con la aplicación y esté listo para volver a intentar reconocer el gesto.

  2. Ahora que hemos definido un reconocedor de gestos personalizados (CheckmarkGestureRecognizer), edite el archivo CustomGestureViewController.cs y agregue las dos variables de instancia siguientes:

    #region Private Variables
    private bool isChecked = false;
    private CheckmarkGestureRecognizer checkmarkGesture;
    #endregion
    
  3. Para crear una instancia y configurar nuestro reconocedor de gestos, agregue el siguiente método al controlador:

    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. Edite ViewDidLoad para que llame a WireUpCheckmarkGestureRecognizer, como se muestra en el siguiente fragmento de código:

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
    
        // Wire up the gesture recognizer
        WireUpCheckmarkGestureRecognizer();
    }
    
  5. Ejecute la aplicación e intente dibujar una "V" en la pantalla. Debería ver que la imagen que se muestra cambia, como se muestra en las capturas de pantalla siguientes:

    El botón activado

    El botón desactivado

Las tres secciones anteriores mostraron diferentes formas de responder a eventos táctiles en iOS: mediante eventos táctiles, reconocedores de gestos integrados o con un reconocedor de gestos personalizados.