Search code examples
swifttouchesmoved3dtouch

Swift SpriteKit 3D Touch and touches moved


I have recently send out a version of my game to TestFlight for internal testing. My friend noticed that on his iPhone 6S Plus the controls to shoot do not work correctly.

The controls are as follow

1) left half of screen = jump

2) right half of screen = shoot

3) Slide and hold on right half of screen = start slow motion

Now in my touches began method I have a slight delay for the shooting, so if touches moved is called it cancels the shooting and starts slow mo. This seems to be causing the problem on 3D Touch enabled phones.

Digging further into this I came across this post

iOS 9 Spritekit Double Tap Not Working on iPhone 6S

where it basically states that on 3D Touch enabled devices the touches moved method gets called automatically.

  On 3D Touch capable devices, touch pressure changes cause     
   touchesMoved:withEvent: to be called for all apps running on iOS   
   9.0. When running iOS 9.1, the method is called only for apps  
   linked with the iOS 9.0 (or later) SDK.

    Apps should be prepared to receive touch move events with no   
    change in the x/y coordinates. "

This seems to make sense of why my friend has problems, if touches moved is always called it will cancel the shooting and start slow mo, even though he is not sliding on the screen.

So my questions is

1) Can you disable 3D Touch in SpriteKit games?

2) Is this a bug?

3) any other way to use touches moved correctly on 3D Touch devices?

4) alternatively can I use gesture recognisers to simulate a slide and hold feature that I am currently doing in touches moved.

Thanks for any help


Solution

  • After trying various other things that didnt work, including UISwipeGestureRecognizer subclasses, I finally figured it out.

    The answer was actually staring me right in the face in that same stackOverflow post I linked in my question

    iOS 9 Spritekit Double Tap Not Working on iPhone 6S

    The solution is to create a property

    var startingTouchLocation: CGPoint?
    

    than in touches Began you set that property

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch in touches {
            let location = touch.locationInNode(self)
    
            /// Set starting touch. this is for 3d touch enabled devices, see touchesMoved method below
            startingTouchLocation = location
    
    
            ....
         }
     }
    

    and than add a check in touchesMoved to return from the method until the touch has actually moved.

     override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch in touches {
            let location = touch.locationInNode(self)
    
            /// WAY 1 - Check if touch moved because on 3d touch devices this method gets called on a force touch.
            guard location != startingTouchLocation else { return }
    
            // Your code
            ...
    
            /// WAY 2 - Add a min distance for the finger to travel in any direction before calling touches moved code
            let minValue: CGFloat = 30
            guard let startingLocation = startingLocation else { return }
    
            guard currentLocation.y < startingLocation.y - minValue ||
            currentLocation.y > startingLocation.y + minValue ||
            currentLocation.x < startingLocation.x - minValue ||
            currentLocation.x > startingLocation.x + minValue else { return  false }
    
           // Your code 
           ....
         }
      }