PennyPincher
Penny Pincher is a fast template-based gesture recognizer, developed by Eugene Taranta and Joseph LaViola (full paper reference below). The algorithm is well-suited for mobile applications since it is both fast and accurate and, as shown in the evaluation by the authors, outperforms other recognizers. This project provides a Swift implementation of Penny Pincher and shows its usage in a simple example project. Also, the framework contains a UIGestureRecognizer subclass that integrates well into the existing gesture recognition framework of iOS.
Here's the full reference for the paper:
Eugene M. Taranta, II and Joseph J. LaViola, Jr.. 2015. Penny pincher: a blazing fast, highly accurate $-family recognizer. In Proceedings of the 41st Graphics Interface Conference (GI '15). Canadian Information Processing Society, Toronto, Ont., Canada, Canada, 195-202.
Demo
Requirements
v1.2.0: >= iOS 9, Xcode 8, Swift 3.1
v1.0.3: >= iOS 8, Xcode 7, Swift 2.0
Installation
Recommended installation options are via Carthage or manual installation.
Carthage:
PennyPincher supports installation via Carthage:
- Add the following line to your Cartfile:
github "fe9lix/PennyPincher" >= 1.2
- Run
carthage update
Manual:
- Drag the folder
PennyPincherExample/Carthage/Build/iOS/PennyPincher.framework
into your Xcode project and select "Copy items if needed". - Make sure that the framework is added under
Embedded Binaries
in the general section of your project's target settings.
Usage
Please see the ViewController
of the example project on how to use PennyPincher. Although you can use the PennyPincher
class directly, the easiest way is to instantiate its gesture recognizer class, configure it, and add it to a view:
let pennyPincherGestureRecognizer = PennyPincherGestureRecognizer()
pennyPincherGestureRecognizer.enableMultipleStrokes = true
pennyPincherGestureRecognizer.allowedTimeBetweenMultipleStrokes = 0.2
pennyPincherGestureRecognizer.cancelsTouchesInView = false
pennyPincherGestureRecognizer.addTarget(self, action: "didRecognize:")
view.addGestureRecognizer(pennyPincherGestureRecognizer)
In the code above, the following properties are set:
enableMultipleStrokes
: Allows gestures to be composed of multiple separate strokes, as long as the pause between strokes does not exceedallowedTimeBetweenMultipleStrokes
. When the property is set tofalse
, the gesture recognizer transitions to the cancelled state as soon as the user lifts the finger.allowedTimeBetweenMultipleStrokes
: See above.cancelsTouchesInView
: Regular iOS gesture recongizer property. Might be set tofalse
when you want to ensure that touches are still delivered to the attached view.
The target-action pair is executed for state changes triggered by the recognizer. You can use the state
property to react accordingly in the UI. The result
property returns a tuple consisting of the recognized PennyPincherTemplate
and CGFloat value indicating the similarity. For example:
guard let (template, similarity) = pennyPincherGestureRecognizer.result else {
print("Could not recognize.")
return
}
let similarityString = String(format: "%.2f", similarity)
print("Template: \(template.id), Similarity: \(similarityString)")
You can add and remove templates by modifying the templates
array property of the recognizer. The PennyPincher
class provides a static method to create new templates of type PennyPincherTemplate
(a struct). Required parameters are the id
(a unique string) and points
(an array of CGPoints).
For example:
let template = PennyPincher.createTemplate("templateID", points: points)
pennyPincherGestureRecognizer.templates.append(template)
Templates could be serialized and saved to disk and then loaded again into memory when the application launches. PennyPincher works pretty well with only one template per gesture (id
) but, depending on your use case, you can increase its accuracy by adding more for each gesture.
Loading Templates
PennyPincher comes with an importer to load and parse the binary Android gesture file format (thanks to @rafcabezas for the contribution). Pass the URL of the gestures file to AndroidGesturesImporter.translatedGestures(from:)
and then add the templates to the gesture recognizer. The example project contains a gestures file with two gestures.
let gestures = AndroidGesturesImporter.translatedGestures(from: gesturesFile)
for gesture in gestures {
if let template = PennyPincher.createTemplate(gesture.id, points: gesture.allPoints) {
pennyPincherGestureRecognizer.templates.append(template)
}
}
Author
fe9lix
Contributors
rafcabezas
License
PennyPincher is available under the MIT license. See the LICENSE file for more info.