Пошаговое руководство. Использование сенсорного ввода в Xamarin.iOS

В этом пошаговом руководстве показано, как писать код, реагирующий на различные виды событий касания. Каждый пример содержится на отдельном экране:

  • Примеры касания — как реагировать на события касания.
  • Примеры распознавателя жестов — как использовать встроенные распознаватели жестов.
  • Пример распознавателя пользовательских жестов — создание пользовательского распознавателя жестов.

Каждый раздел содержит инструкции по написанию кода с нуля.

Следуйте приведенным ниже инструкциям, чтобы добавить код в раскадровку и узнать о различных типах событий касания, доступных в iOS. Кроме того, откройте готовый пример , чтобы увидеть все, что работает.

Примеры касания

В этом примере мы продемонстрируем некоторые интерфейсы API сенсорного ввода. Выполните следующие действия, чтобы добавить код, необходимый для реализации событий сенсорного ввода:

  1. Откройте проект Touch_Start. Сначала запустите проект, чтобы убедиться, что все хорошо, и нажмите кнопку "Примеры касания". Вы должны увидеть экран, аналогичный следующему (хотя ни одна из кнопок не будет работать):

    Sample app run with non-working buttons

  2. Измените файл TouchViewController.cs и добавьте в класс 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 это верно, то мы знаем, что пользователь имеет палец DragImage и перемещает его вокруг. Код будет перемещаться DragImage , когда пользователь перемещает пальцем по экрану.

  4. Нам нужно справиться с ситуацией, когда пользователь поднимает пальцем с экрана, или 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 на экране.

  5. На этом этапе будет завершен экран "Примеры сенсорного ввода". Обратите внимание, как экран изменяется при взаимодействии с каждым из изображений, как показано на следующем снимке экрана:

    The starting app screen

    The screen after the user drags a button

Примеры распознавателя жестов

В предыдущем разделе показано, как перетащить объект по экрану с помощью событий сенсорного ввода. В этом разделе мы избавимся от событий касания и покажем, как использовать следующие распознаватели жестов:

  • Для 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;
        }
    }
    

    Приведенный выше код сначала проверка состояние распознавателя жестов, а затем переместить изображение по экрану. В этом коде контроллер теперь может поддерживать перетаскивание одного изображения на экране.

  4. Добавьте объект UITapGestureRecognizer , который изменит изображение, отображаемое в DoubleTouchImage. Добавьте в контроллер следующий метод 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. Запустите приложение и взаимодействуйте с двумя изображениями. На следующем снимках экрана показан один из примеров этих взаимодействий:

    This screenshot shows a drag interaction

Распознаватель пользовательских жестов

В этом разделе мы будем применять основные понятия из предыдущих разделов для создания пользовательского распознавателя жестов. Распознаватель пользовательских жестов будет подклассами UIGestureRecognizerи распознает, когда пользователь рисует "V" на экране, а затем переключает растровое изображение. На следующем снимок экрана показан пример этого экрана:

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

Выполните следующие действия, чтобы создать распознаватель пользовательских жестов:

  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 и добавьте следующие две переменные экземпляра:

    #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. Измените ViewDidLoad его так, чтобы он вызывается WireUpCheckmarkGestureRecognizer, как показано в следующем фрагменте кода:

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
    
        // Wire up the gesture recognizer
        WireUpCheckmarkGestureRecognizer();
    }
    
  5. Запустите приложение и попробуйте нарисовать "V" на экране. Вы должны увидеть отображаемое изображение, как показано на следующих снимках экрана:

    The button checked

    The button unchecked

Приведенные выше три раздела демонстрируют различные способы реагирования на события касания в iOS: использование событий сенсорного ввода, встроенных распознавателей жестов или пользовательского распознавателя жестов.