Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-81042

iPhone: create QSwipeGestures with 1 finger, not 3

    XMLWordPrintable

Details

    • Suggestion
    • Resolution: Unresolved
    • P3: Somewhat important
    • None
    • 5.14.0, 6.5
    • None
    • iOS/tvOS/watchOS

    Description

      Hi, doing my first iOS Qt app, and today I tried swiping some QWidgets on my iPhone. While Qt's swipe gestures work fine on my Mac and Windows 10, on the phone it's really tricky to swipe with 3 fingers. Most apps use 1 finger for swipes, seems to be standard on all phones.

      Turns out there's an iOS API for recognizing swipes with 1 finger (also with no churn, been the same for 10 years), and it's pretty trivial to integrate that API with Qt's existing QGesture infrastructure.

      I ended up with a small .mm file with a function that I call from my QMainWindow's ctor, that registers Apple's swipe API into Qt's QUIView, and in the callback from iOS I post a matching QSwipeGesture. So now I can swipe my widgets with 1 finger on the phone

      Would it be possible to add this functionality to Qt itself? As I said, it seems pretty easy, here's the function and the Objective-C method I inject into QUIView (you call the function with your QMainWindow's this and the swipe direction you wish to receive an event for):

      bool registerForIOSSwipes(QWidget* w, QSwipeGesture::SwipeDirection sd)
      {
      // cast the widget's winId (it's our ticket into the iOS underworld)
          auto uv = reinterpret_cast<UIView*>(w->winId());
      
      // what we really like is a ptr to Qt's UIView (QUIView)
          if ("QUIView" != QString::fromNSString(NSStringFromClass([uv class])))
              return false;   // but it was not to be
      
      // have a view, use a lambda to add the 4 different types of swipe gestures
          auto registerSwipe = [uv](UISwipeGestureRecognizerDirection d)
          {
          // new up an iOS gesture recognizer with required # of touches = 1 (Apple default)
              UISwipeGestureRecognizer* sgr = [[UISwipeGestureRecognizer alloc] initWithTarget:uv action:@selector(handleSwipe:)];
              [sgr setDirection:d];
              [uv addGestureRecognizer:sgr];
          };
      
          if (sd & QSwipeGesture::Left )
              registerSwipe(UISwipeGestureRecognizerDirectionLeft );
      
          if (sd & QSwipeGesture::Right)
              registerSwipe(UISwipeGestureRecognizerDirectionRight);
      
          if (sd & QSwipeGesture::Up   )
              registerSwipe(UISwipeGestureRecognizerDirectionUp   );
      
          if (sd & QSwipeGesture::Down )
              registerSwipe(UISwipeGestureRecognizerDirectionDown );
      
      // that's all
          return true;
      }
      
      // some Objective-C red tape so we can inject our gesture recognizer function into Qt's QUIView
      @interface QUIView : UIView @end    // (copied from quiview.h)
      
      @implementation QUIView (RedTapeCategory) - (void) handleSwipe: (UISwipeGestureRecognizer*) swipe
      {
      // got a swipe, synthesize a swipe flavored Qt gesture event and post it
          auto sg = new QSwipeGesture;
      
          if (UISwipeGestureRecognizerDirectionLeft  == swipe.direction)
              sg->setSwipeAngle(180);
      
          if (UISwipeGestureRecognizerDirectionRight == swipe.direction)
              sg->setSwipeAngle(0);
      
          if (UISwipeGestureRecognizerDirectionUp    == swipe.direction)
              sg->setSwipeAngle(90);
      
          if (UISwipeGestureRecognizerDirectionDown  == swipe.direction)
              sg->setSwipeAngle(270);
      
          auto hotSpot = [swipe locationInView:self];
          sg->setHotSpot(QPointF(hotSpot.x,hotSpot.y));
      
      // bon voyage
          qApp->postEvent(qApp->activeWindow(),new QGestureEvent(QList<QGesture*>{sg}));
      }
      @end
      

      Then in my app I receive a normal QSwipeGesture event, say with code like this:

      bool MainWindow::event(QEvent *event)
      {
          if (event->type() != QEvent::Gesture)
              return QWidget::event(event);
      
          auto e = static_cast<QGestureEvent*>(event);
          auto g = e->gesture(Qt::SwipeGesture);
          if (nullptr == g)
              return QWidget::event(event);
      
          auto s = static_cast<QSwipeGesture*>(g);
          qDebug() << s->hotSpot() << s->horizontalDirection() << s->verticalDirection() << "Z";
      
          return true;
      }
      

      (I've trimmed some error checking and skipped the #includes/#imports)

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            hskoglund Henry Skoglund
            Votes:
            3 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes