Návod: Používání dotykového ovládání v Xamarin.iOS

Tento názorný postup ukazuje, jak napsat kód, který reaguje na různé druhy dotykových událostí. Každý příklad je obsažený na samostatné obrazovce:

  • Dotykové ukázky – jak reagovat na dotykové události.
  • Ukázky rozpoznávání gest – jak používat integrované rozpoznávání gest.
  • Ukázka vlastního rozpoznávání gest – jak vytvořit vlastní rozpoznávání gest.

Každá část obsahuje pokyny k napsání kódu úplně od začátku.

Podle následujících pokynů přidejte kód do scénáře a seznamte se s různými typy dotykových událostí dostupných v iOSu. Případně otevřete hotovou ukázku, abyste viděli všechno, co funguje.

Ukázky dotykového ovládání

V této ukázce si ukážeme některá dotyková rozhraní API. Pokud chcete přidat kód potřebný k implementaci dotykových událostí, postupujte takto:

  1. Otevřete Touch_Start projektu. Nejprve spusťte projekt, abyste měli jistotu, že je všechno v pořádku, a klepněte na tlačítko Vzorky dotykového ovládání. Měla by se zobrazit obrazovka podobná následující (i když žádná z tlačítek nebude fungovat):

    Sample app run with non-working buttons

  2. Upravte soubor TouchViewController.cs a přidejte do třídy TouchViewControllernásledující dvě proměnné instance:

    #region Private Variables
    private bool imageHighlighted = false;
    private bool touchStartedInside;
    #endregion
    
  3. Implementujte metodu TouchesBegan , jak je znázorněno v následujícím kódu:

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

    Tato metoda funguje tak, že vyhledá UITouch objekt a pokud existuje, provede nějakou akci na základě místa, kde došlo k dotyku:

    • Uvnitř TouchImage – zobrazí text Touches Began v popisku a změní obrázek.
    • Uvnitř DoubleTouchImage – změňte obrázek zobrazený, pokud gesto bylo poklepání.
    • Uvnitř DragImage – nastavte příznak označující, že se dotykové ovládání spustilo. Tato metoda TouchesMoved použije tento příznak k určení, jestli DragImage se má pohybovat po obrazovce nebo ne, jak je vidět v dalším kroku.

    Výše uvedený kód se zabývá pouze individuálními dotyky, neexistuje žádné chování, pokud uživatel posouvá prst na obrazovce. Pokud chcete reagovat na přesun, implementujte TouchesMoved ho, jak je znázorněno v následujícím kódu:

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

    Tato metoda získá UITouch objekt a pak zkontroluje, kde došlo k dotyku. Pokud k dotykovému ovládání došlo TouchImage, zobrazí se na obrazovce text Touches Moved (Přesunuto).

    Pokud touchStartedInside je pravda, pak víme, že uživatel má prst na ruce DragImage a pohybuje se kolem. Kód se přesune DragImage , když uživatel posouvají prst po obrazovce.

  4. Musíme zpracovat případ, kdy uživatel zvedne prst z obrazovky nebo iOS zruší dotykovou událost. V tomto případě budeme implementovat TouchesEnded a TouchesCancelled jak je znázorněno níže:

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

    Obě tyto metody resetují touchStartedInside příznak na false. TouchesEnded zobrazí se také TouchesEnded na obrazovce.

  5. V tuto chvíli je obrazovka s dotykovými ukázkami hotová. Všimněte si, jak se obrazovka mění při interakci s jednotlivými obrázky, jak je znázorněno na následujícím snímku obrazovky:

    The starting app screen

    The screen after the user drags a button

Ukázky rozpoznávání gest

Předchozí část ukázala, jak přetáhnout objekt po obrazovce pomocí dotykových událostí. V této části se zbavíme dotykových událostí a ukážeme, jak používat následující rozpoznávání gest:

  • Při UIPanGestureRecognizer přetahování obrázku po obrazovce.
  • Odpověď UITapGestureRecognizer na poklepáním na obrazovku

Při implementaci rozpoznávání gest postupujte takto:

  1. Upravte soubor GestureViewController.cs a přidejte následující proměnnou instance:

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

    Tuto proměnnou instance potřebujeme, abychom mohli sledovat předchozí umístění image. Rozpoznávání gest posunu použije originalImageFrame hodnotu k výpočtu posunu potřebného k překreslení obrázku na obrazovce.

  2. Přidejte do kontroleru následující metodu:

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

    Tento kód vytvoří UIPanGestureRecognizer instanci a přidá ji do zobrazení. Všimněte si, že k gestu ve formě metody HandleDrag přiřadíme cíl – tato metoda je k dispozici v dalším kroku.

  3. Pokud chcete implementovat HandleDrag, přidejte do kontroleru následující kód:

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

    Výše uvedený kód nejprve zkontroluje stav rozpoznávání gest a pak obrázek přesune po obrazovce. S tímto kódem teď může ovladač podporovat přetahování jednoho obrázku po obrazovce.

  4. Přidejte obrázek UITapGestureRecognizer , který změní obrázek zobrazený v DoubleTouchImage. Přidejte do kontroleru následující metodu 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);
    }
    

    Tento kód je velmi podobný kódu pro kód, UIPanGestureRecognizer ale místo použití delegáta pro cíl, který používáme Action.

  5. Poslední věcí, kterou musíme udělat, je upravit ViewDidLoad tak, aby volaly metody, které jsme právě přidali. Změňte ViewDidLoad tak, aby připomínal následující kód:

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

    Všimněte si také, že inicializujeme hodnotu originalImageFrame.

  6. Spusťte aplikaci a komunikujte se dvěma imagemi. Následující snímek obrazovky je jedním z příkladů těchto interakcí:

    This screenshot shows a drag interaction

Rozpoznávání vlastních gest

V této části použijeme koncepty z předchozích částí k vytvoření vlastního rozpoznávání gest. Rozpoznávání vlastních gest podtřídy UIGestureRecognizera rozpozná, když uživatel na obrazovce nakreslí "V" a pak přepne rastrový obrázek. Následující snímek obrazovky je příkladem této obrazovky:

The app will recognize when the user draws a V on the screen

Pokud chcete vytvořit vlastní rozpoznávání gest, postupujte takto:

  1. Přidejte do projektu CheckmarkGestureRecognizernovou třídu a nastavte ji jako následující kód:

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

    Metoda Reset je volána při State změně vlastnosti buď Recognized nebo Ended. Toto je čas resetovat všechny interní stavy nastavené ve vlastním rozpoznávání gest. Teď může třída začít znovu začít znovu, až uživatel s aplikací komunikuje a bude připraven znovu zjistit gesto.

  2. Teď, když jsme definovali vlastní rozpoznávání gest (CheckmarkGestureRecognizer) upravte soubor CustomGestureViewController.cs a přidejte následující dvě proměnné instance:

    #region Private Variables
    private bool isChecked = false;
    private CheckmarkGestureRecognizer checkmarkGesture;
    #endregion
    
  3. Pokud chcete vytvořit instanci a nakonfigurovat rozpoznávání gest, přidejte do kontroleru následující metodu:

    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. Upravte ViewDidLoad tak, aby volaly WireUpCheckmarkGestureRecognizer, jak je znázorněno v následujícím fragmentu kódu:

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
    
        // Wire up the gesture recognizer
        WireUpCheckmarkGestureRecognizer();
    }
    
  5. Spusťte aplikaci a zkuste na obrazovce nakreslit "V". Měla by se zobrazit změna obrázku, jak je znázorněno na následujících snímcích obrazovky:

    The button checked

    The button unchecked

Výše uvedené tři části ukazují různé způsoby reakce na dotykové události v iOSu: použití dotykových událostí, integrovaných rozpoznávání gest nebo vlastního rozpoznávání gest.