Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. iOS App in QML how to navigate backward through StackView via swipe gestures
Forum Updated to NodeBB v4.3 + New Features

iOS App in QML how to navigate backward through StackView via swipe gestures

Scheduled Pinned Locked Moved Solved QML and Qt Quick
8 Posts 3 Posters 3.1k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    eumel1990
    wrote on 20 Oct 2016, 08:13 last edited by
    #1

    Hi together,

    I am developing an crossplatform compatible (iOS, Android; maybe later also windows phone) app prototype for our company. I am completely new to QML programming.

    In my main.qml file I use a StackView which is used for the basic navigation through views in the app. For the android like backward navigation I can simply listen to the "Keys.onReleased" event to pop the current view.

    For the iOS implementation of the app I want to use the swipe gesture for navigating to the previous view. What way would you suggest to achieve such functionality?

    I currently have an (not completely working approach) where I am using an "MouseArea" as a child of my StackView (with z: 100) to detect the swipe gesture. The navigation works, but I am in trouble to use the controls (like buttons, list, ...) underneath the MouseArea.

    My current StackView looks like that:

    StackView {
            id: mainStackLayout
            anchors.fill: parent
            focus: true
    
            //handle the android back button
            Keys.onReleased: {
                if (event.key === Qt.Key_Back && mainStackLayout.depth > 1) {
                    mainStackLayout.pop()
                    event.accepted = true
                }
            }
    
            //and the ios back swipe
            MouseArea {
                id: globalMouseArea
                anchors.fill: parent
                hoverEnabled: true
                z: 100
                //propagateComposedEvents: true
                preventStealing: true
                property real velocity: 0.0
                property int xStart: 0
                property int xPrev: 0
                property bool tracing: false
    
                onPressed: {
                    if(mainStackLayout.depth > 1) {
                        xStart = mouse.x
                        xPrev = mouse.x
                        velocity = 0
                        tracing = true
                        mouse.accepted = true
                    } else {
                        mouse.accepted = false
                    }
                }
                onPositionChanged: {
                    print('position changed')
                    //this routine sets the velocity which is checked in the onReleased handler
                    if (!tracing) {
                        mouse.accepted = false
                        return
                    }
                    var currVel = (mouse.x-xPrev)
                    velocity = (velocity + currVel)/2.0
                    xPrev = mouse.x
                    mouse.accepted = true
                }
                onReleased: {
                    print('released')
                    if(!tracing) {
                        mouse.accepted = false
                        return
                    }
                    print('velocity: ' + velocity)
                    tracing = false
                    if (velocity > 15 && mouse.x > parent.width * 0.2 ) {
                        print('Detect swipe')
                        mouse.accepted = true
                        mainStackLayout.pop()
                    }
                }
            }
        }
    

    But as I said it steals the input events from the controls underneath. I tried to get rid of this problem due simulating mouse clicks in case of the user releases the mouse without making an valid gesture, but I failed to implement such a mouse simulating. I tried to send mouseEvents from c++ to several QML objects, but they never received an event.

    For simulating the MouseEvents I tried things like

    QCoreApplication::instance()->sendEvent(findApplicationWindow(), &mouseEvent);
    

    where the mouseEvent is an left click and findApplicationWindow() returns the instance of the "QQuickApplicationWindow". In the net I found something like

        QMouseEvent mouseEvent(QEvent::MouseButtonPress, QPointF(105, 25), Qt::LeftButton, 0, Qt::NoModifier);
    
        QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
        pressEvent.setScenePos(QPointF(105, 25));
        pressEvent.setButton(Qt::LeftButton);
        pressEvent.setButtons(Qt::LeftButton);
        QApplication::sendEvent(object, &pressEvent);
    
        QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
        releaseEvent.setScenePos(QPointF(105, 25));
        releaseEvent.setButton(Qt::LeftButton);
        releaseEvent.setButtons(Qt::LeftButton);
        QApplication::sendEvent(object, &releaseEvent);
    

    Which is although not working.

    But, all in all, I think it is a bad solution to simulate mouse clicks in my case.

    Maybe someone out here has already an working solution for this case or an advise for me. That would really be helpful :-)

    Thank you in advance!

    Greetings
    eumel1990

    1 Reply Last reply
    1
    • P Offline
      P Offline
      p3c0
      Moderators
      wrote on 20 Oct 2016, 08:39 last edited by p3c0
      #2

      Hi @eumel1990

      I currently have an (not completely working approach) where I am using an "MouseArea" as a child of my StackView (with z: 100) to detect the swipe gesture.

      Instead of maintaining the z order you can make MouseArea as the root element and make StackView child of it so that you propagate only those events beneath which you require.

      MouseArea {
         ...
         StackView {
         ...
         }
      }
      

      Can SwipeView be an alternative to StackView in your case ?

      Also Since Qt 5 there is no relation between QGraphics* classes and QtQuick. The backend implemenation is now scenegraph based. So sending events in that way wont work.

      157

      1 Reply Last reply
      2
      • E Offline
        E Offline
        eumel1990
        wrote on 20 Oct 2016, 08:50 last edited by
        #3

        Hi p3c0

        that's brilliant. I just did a short test until now, but changing the parent-child relation like you suggested worked for me.

        Now my StackView looks like that:

        MouseArea {
                id: globalMouseArea
                anchors.fill: parent
                hoverEnabled: true
                //z: 100
                //propagateComposedEvents: true
                preventStealing: true
                property real velocity: 0.0
                property int xStart: 0
                property int xPrev: 0
                property bool tracing: false
        
                onPressed: {
                   // mouse.accepted = false
        
                    //mouseEventSender.sendMouseEvent(mouse.x, mouse.y, globalMouseArea)
        
                    if(mainStackLayout.depth > 1) {
                        xStart = mouse.x
                        xPrev = mouse.x
                        velocity = 0
                        tracing = true
                        mouse.accepted = true
                    } else {
                        mouse.accepted = false
                    }
                }
                onPositionChanged: {
                    print('position changed')
                    //this routine sets the velocity which is checked in the onReleased handler
                    if (!tracing) {
                        mouse.accepted = false
                        return
                    }
                    var currVel = (mouse.x-xPrev)
                    velocity = (velocity + currVel)/2.0
                    xPrev = mouse.x
                    mouse.accepted = true
                }
                onReleased: {
                    print('released')
                    if(!tracing) {
                        mouse.accepted = false
                        return
                    }
                    print('velocity: ' + velocity)
                    tracing = false
                    if (velocity > 15 && mouse.x > parent.width * 0.2 ) {
                        print('Detect swipe')
                        mouse.accepted = true
                        mainStackLayout.pop()
                    } /*else {
                        mouse.accepted = false
                    }*/
                }
        
                StackView {
                    id: mainStackLayout
                    anchors.fill: parent
                    focus: true
        
                    //handle the android back button
                    Keys.onReleased: {
                        if (event.key === Qt.Key_Back && mainStackLayout.depth > 1) {
                            mainStackLayout.pop()
                            event.accepted = true
                        }
                    }
                }
            }
        

        I know the SwipeView, but as far as I understood, it's navigating in a line (like if you swiping through pictures). I am using the StackView for the complete App and, for later implementation steps, the backward swipe should only be enabled for the iOS implementation.

        p3c0 you really helped me a lot, I am really grateful, Thank you!!

        Greetings
        eumel1990

        1 Reply Last reply
        0
        • P Offline
          P Offline
          p3c0
          Moderators
          wrote on 20 Oct 2016, 08:57 last edited by
          #4

          @eumel1990 You're Welcome :)

          157

          1 Reply Last reply
          0
          • E Offline
            E Offline
            eumel1990
            wrote on 20 Oct 2016, 13:38 last edited by
            #5

            Sry, but I have to ask again. What is the reason for this behaviour? Why does the change of the parent/child relation of MouseArea/StackView cause that change of the input event mechanics?

            Can you give an advise for an modern book about QML and/or App development with Qt? I already have "Getting started with Qt Quick" from Paolo Sereno (it's about Qt 5.6) which is like a short tutorial.

            Thanks in advise

            greetings
            eumel1990

            1 Reply Last reply
            0
            • P Offline
              P Offline
              p3c0
              Moderators
              wrote on 20 Oct 2016, 15:55 last edited by
              #6

              @eumel1990

              Sry, but I have to ask again. What is the reason for this behaviour? Why does the change of the parent/child relation of MouseArea/StackView cause that change of the input event mechanics?

              It is better to have a the main Item to listen to mouse events and then propagate those to individual items rather than each item grab the events and propagate them to other child or siblings.

              Can you give an advise for an modern book about QML and/or App development with Qt? I already have "Getting started with Qt Quick" from Paolo Sereno (it's about Qt 5.6) which is like a short tutorial.

              Well I guess most of us here have learned Qt/QML through the excellent official documentation so I would first suggest the docs itself. Another one is
              http://qmlbook.github.io/

              157

              1 Reply Last reply
              1
              • E Offline
                E Offline
                eumel1990
                wrote on 21 Oct 2016, 06:22 last edited by
                #7

                Ok. Thank you for this informations :-)

                1 Reply Last reply
                0
                • alex_spataruA Offline
                  alex_spataruA Offline
                  alex_spataru
                  wrote on 4 Nov 2020, 05:54 last edited by alex_spataru 11 Apr 2020, 05:58
                  #8

                  Sorry for replying to a topic so old, however, I needed to implement this behavior and made some modifications to the code in this forum. To help someone avoid loosing time, I'll post my modifications.

                  Summary of modifications:

                  • Created separate QML item to implement swipe gesture (only enabled on iOS)
                  • Played with the "mouse.accepted" assignments to get same behavior without drastically affecting full-page Flickable-based items
                  • Instead of filling the whole stack view, we create the object after the rest of the UI items, and anchor it to the left part of the screen.
                  • We set a relatively small width to avoid issues with Flickables, ListViews, GridViews, etc.

                  First, I created a new QML file named BackGestureDetector.qml, with the following code:

                  import QtQuick 2.12
                  
                  MouseArea {
                      width: 32
                      hoverEnabled: true
                      scrollGestureEnabled: false
                      propagateComposedEvents: true
                      enabled: Qt.platform.os === "ios"
                  
                      signal backGestureDetected()
                  
                      property int xPrev: 0
                      property int xStart: 0
                      property real velocity: 0.0
                      property bool tracing: false
                      property bool allowedToWork: false
                  
                      onPressed: {
                          if (allowedToWork) {
                              xStart = mouse.x
                              xPrev = mouse.x
                              velocity = 0
                              tracing = true
                              mouse.accepted = true
                          }
                  
                          else
                              mouse.accepted = false
                      }
                  
                      onPositionChanged: {
                          if (!tracing) {
                              mouse.accepted = false
                              return
                          }
                  
                          var currVel = (mouse.x - xPrev)
                          velocity = (velocity + currVel)/2.0
                          xPrev = mouse.x
                  
                          mouse.accepted = false
                      }
                  
                      onReleased: {
                          if (!tracing) {
                              mouse.accepted = false
                              return
                          }
                  
                          tracing = false
                  
                          if (velocity > 15 && mouse.x > parent.width * 0.2)
                              backGestureDetected()
                  
                          mouse.accepted = false
                      }
                  }
                  

                  Finally, I integrate it to the UI as follows:

                  import QtQuick 2.12
                  import QtQuick.Window 2.12
                  import QtQuick.Controls 2.12
                  
                  ApplicationWindow {
                      id: app
                  
                      //
                      // Set window geometry
                      //
                      width: 800
                      height: 600
                      visible: true
                      title: qsTr("Swipe gestures example")
                  
                      //
                      // Page display
                      //
                      StackView {
                          id: stackView
                          focus: true
                          initialItem: page1
                          anchors.fill: parent
                  
                          Page {
                              id: page1
                              visible: false
                              width: parent.width
                              height: parent.height
                  
                              Button {
                                  text: qsTr("Go to page 2")
                                  anchors.centerIn: parent
                                  onClicked: stackView.push(page2)
                              }
                          }
                  
                          Page {
                              id: page2
                              visible: false
                              width: parent.width
                              height: parent.height
                  
                              Label {
                                  text: qsTr("Page 2")
                                  anchors.centerIn: parent
                              }
                          }
                      }
                  
                      //
                      // Swipe back to navigate
                      //
                      BackGestureDetector {
                          allowedToWork: stackView.depth > 1
                          onBackGestureDetected: stackView.pop()
                  
                          anchors {
                              top: parent.top
                              left: parent.left
                              bottom: parent.bottom
                          }
                      }
                  }
                  

                  Any comments are welcome, hope everyone is safe regarding the pandemic.

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved