Search code examples
swifttouch

Continuous Touch in Swift


The goal is to perform an action every .5 seconds for as long as a touch is maintained on a button, and to cease that action when the touch has ended. Following advice from other posts here in SO, I have employed a strategy of leveraging a boolean which is set to true on touchDown, and set to false on touchUp. However, it seems that the touchUp func is either not setting the boolean, or the touchDown is ignoring it, because when I lift my finder off the button the action continues.

Here is my touches code:

// MARK: - Touches

func touchDown(atPoint pos : CGPoint) {
    print("Touched Down in \(purpose) down arrow")

    arrowIsPressed = true
    self.texture = SKTexture(imageNamed: "bottomArrowPressed.png")

    adjustBlue() // Because you want one immediate action, in case there is no continuous touch

    while arrowIsPressed == true {
        perform(#selector(callback), with: nil, afterDelay: 1.0)
    }
}

func touchUp(atPoint pos : CGPoint) {

    arrowIsPressed = false
    self.texture = SKTexture(imageNamed: "bottomArrow.png")

    adjustCounter = 0
}

// MARK: - Touch Helpers

@objc func callback() {

    self.adjustBlue()
}


func adjustBlue() {
    adjustCounter += 1
    print("Adjust Action \(adjustCounter)")
    // eventually useful code will go here
}

Please note that it is a smallish button and I am not concerned at all bout tracking multiple touches.

Where is my logic failing? I feel that this is a fairly straight forward problem and solution but I am doing something wrong and I cannot fathom what.

UPDATE: by placing a print statement inside the while loop I see that the while loop is being called extremely rapidly over and over, even though I have a perform statement inside it with a .5 second delay. Not sure why...


Solution

  • My intuition screams that your while loop blocks the main thread.

    The while does not delay between iterations, instead the calling of the callback gets delayed. There are no delays between loops so the main thread gets blocked and the touchup function has no occasion to run.

    You could change this to

    func touchDown(atPoint pos : CGPoint) {
         print("Touched Down in \(purpose) down arrow")
    
        arrowIsPressed = true
         self.texture = SKTexture(imageNamed: "bottomArrowPressed.png")
    
         adjustBlue() // Because you want one immediate action, in case there is no continuous touch
    
         callback()
     }
    
    func touchUp(atPoint pos : CGPoint) {
    
        arrowIsPressed = false
        self.texture = SKTexture(imageNamed: "bottomArrow.png")
    
        adjustCounter = 0
    }
    
    // MARK: - Touch Helpers
    
    @objc func callback() {
          adjustBlue()
    
        // loop in a non blocking way after 0.5 seconds
          DispatchQueue.main.asynAfter(deadline: .now() + 0.5) {
                if self.arrowIsPressed {
                   self.callback()
                }
           }
    }
    
    
    
    func adjustBlue() {
        adjustCounter += 1
        print("Adjust Action \(adjustCounter)")
        // eventually useful code will go here
    }