Sorry your touch was moved

It was a simple task to capture touches on a view, you simply had three basic functions, touchesBegan, touchesMoved and touchesEnded. So if you started to monitor from a touchesBegan without encountering a touchesMoved, it meant somebody had a long press. Simple and easy, however this has all changed.
Apple has constantly been releasing specialised hardware and the SDK has provided ways to access it. Accessing touch was one of the simplest one as described above. Prior to gestures being introduced, the way to recognize swipe was to set a flag that the touch started on touchesBegan and then check where it ended and then based on the ∆X or the ∆Y, which ever is larger, would indicate if it was a horizontal or a vertical swipe. The sign would indicate if the swipe was left or right, up or down. To capture diagonal swipes, you could check the ∆'s a larger number of both would indicate a diagonal swipe. Another way could be to find the angle between the start touch point and the end touch point.

If you wanted to recognize a long press, it would start with a touchesBegan and set up a timer, and when the timer fires and there was no move and neither cancelled or ended, it meant a long press.

This was the way most developers wrote their code. It worked and continued to work, however these were in the 32-bit days of iOS. Now that Apple hardware is all 64-bit and the clean-up on the Apple Store, Apple reached out to the developers telling them to update their apps that were not updated for a while. I had a large number of apps on the store, so I got a lot of emails from Apple and then followed by notifications telling me that an app has been removed (actually a lot of notifications).

One embarrassing issue that I found impacting my app was relating to a sound file embedded into an earlier version of the iOS SDK. This was causing some of my apps to crash that were using that sound file. In hindsight, this was not a good idea to reference an embedded sound file. However apart from that, the apps would work perfectly fine, compiled with iOS 3.x they continued to work on iOS 10.x. Many of the earlier apps were compiled using XCode and written in Objective-C then other apps were written using CoronaSDK. The first thing that happened with CoronaSDK was they released Graphics 2.0 which kind of broke older source code and updating the source code was worth only for those that had apps getting them a substantial income off the app store.

Back to the point, Trying to fix one of the the apps, it involved recompiling it with a newer version of Xcode for supporting 64-bit. This brought to light an interesting problem. The app when downloaded from the app store worked perfectly fine. When the same code was recompiled with Xcode 8.0 the app would not work as expected. The issue was the detection of a long press. The way a long press was determined is explained above. It was a bit of an uphill drive to determine how and why this was happening. The resolution was found by the fact that the same app worked as expected on an iPhone 6+ as expected but failed on an iPhone 6S. So there was something on the S that was causing the app to fail. The only thing that is different between the 6 and the 6S is the hardware with Force touch.

This is where the interesting thing came to light. I created a simple view with three functions as such
import UIKit

class MyView: UIView {
    
    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        print("Began>>>>\n")
        if let touch = touches.first {
            print(touch.force)
        }
    }
    
    override func touchesMoved(_ touches: Set, with event: UIEvent?) {
        print("Moved>>>>\n")
    }
    
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        print("Ended>>>>\n")
    }
    
    override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
        print("Cancelled>>>>\n")
    }
}
then create a new view that uses this as
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let vue = MyView(frame: CGRect(x:50, y:50, width: 250, height: 250))
        vue.backgroundColor = UIColor.lightGray
        
        self.view.addSubview(vue)
    }

If you run this code on a mobile device without the force touch, it works perfectly fine. However you would find that the moment you touch the screen on an iPhone with the force touch hardware, the moment you touch the view, it triggers the touchesBegan but also triggers the touchesMoved. This was a bit strange as the touch was not moved at all.

As it turns out with force touch, the moment there is the slightest of pressure the system renders your touch as moved. While many of the frameworks that abstract this do not check for force (as yet). So if you are using a framework that does this abstraction, you need to consider this little nuance where your touch is moved even when you haven't really moved it.

Comments

Popular Posts