Projektbeschreibung

Fachartikel von  Dirk Steinkopf, erschienen in Web & Mobile Developer #4, November 2012

Unterschreiben mit dem Finger

Vertragsmanagement spielt die Unterschrift eine wichtige Rolle. Mit Smartphones wie dem iPhone können Dokumente direkt mit dem Finger unterschrieben werden.

Bei Abschluss oder Kündigung eines Vertrages wird fast immer noch ein mit der Hand unterschriebenes Dokument erwartet. Für die elektronische Welt bedeutet dies meist einen Medienbruch: Man druckt ein Dokument aus, unterschreibt es und schickt es in Papierform oder – nachdem man es gescannt hat – elektronisch weiter. Dieser Medienbruch ist ein Umweg, der für den Benutzer keinen merklichen Vorteil, sondern nur Umstände bedeutet. Moderne mobile Geräte haben fast alle einen Touchscreen, mit dem die Benutzer sehr vertraut sind. Was liegt da näher als eine Unterschrift mit dem Finger direkt auf dem Gerät? Dadurch können Dokumente am Gerät erstellt oder bearbeitet und „in einem Rutsch“ unterschrieben und
weitergeleitet werden.
Für die Realisierung einer solchen Unterschriftsfunktion heißt es allerdings etwas genauer nachdenken, sonst bekommt der Benutzer statt einer Unterschrift nur ein unleserliches Gekrakel, das eher an Pavian-Kunst erinnert als an eine Unterschrift. Das Prinzip ist bei modernen mobilen Plattformen fast immer das gleiche: Der Entwickler muss in einem irgendwie gearteten Eventhandler auf die Touch/Motion-Events reagieren und
das Ergebnis in Echtzeit, das heißt möglichst ohne Zeitverzögerung, darstellen. Am Beispiel von iOS wollen wir uns die optimale Lösung genauer ansehen.
Dabei sind grundsätzlich folgende Dinge zu beachten:
  • Die Events müssen schnell genug verarbeitet werden, damit eine flüssige Benutzung sichergestellt ist.
  • Die Touch-Event-Methoden (unter iOS touchesBegan, touchesMoved und touchesEnded) werden von einem Thread aufgerufen, in dem keine Bildschirmausgaben gemacht werden dürfen.

Eine häufig verwendete Technik ist, eine zunächst nicht angezeigte („Offline“-) Bitmap anzulegen, in der die Bildschirmausgaben vorbereitet werden. Wenn alles so weit ist, wird die Offline-Bitmap dann 1:1 auf den Bildschirm kopiert.

In unserem Kontext heißt das Folgendes:

  • Am Anfang wird die Offline-Bitmap erzeugt (CGBitmapContextCreate).
  • Bei jedem Event wird nun das gerade vom Benutzer „abgefahrene“ Linienstück in die Offline-Bitmap gezeichnet (CGContextAddLineTo- Point und CGContextStrokePath).
  • Durch Aufruf von setNeedsDisplay wird das Neuzeichnen veranlasst.

Bei diesem Prinzip, das in vielen anderen Bereichen erfolgreich eingesetzt wird, zeigt sich hier jedoch folgendes Problem: Die Unterschrift sollte – damit sie sich gut verwenden lässt – einen möglichst großen Bereich einnehmen, am besten den ganzen Bildschirm. Das heißt, dass gerade auf Geräten mit hoher Auflösung relativ hohe Datenmengen verarbeitet werden müssen. Dadurch wird die Verarbeitung zu langsam, um flüssig abzulaufen. Bei einer beispielhaften Implementierung dieser Lösung sieht man, dass das Ergebnis auf allen Geräten sehr schlecht ist. iPhone 4 und iPad erweisen sich wegen der hohen Display-Auflösung als schlechter als iPhone 3GS und sogar als das alte 3G. Bild 1 zeigt einen Screenshot mit dem nicht zufriedenstellenden Ergebnis auf dem iPhone 4.

Vorbild Spieleprogrammierung

Sucht man eine gute Lösung, hilft es oft, von anderen Bereichen zu lernen. So auch hier: Das Zeichnen der Unterschrift hat viel Ähnlichkeit mit einem Spiel: Der Nutzer „steuert“ mit seinem Finger die Spielfigur (die Linie) und erwartet, dass seine Bewegung möglichst ohne Verzögerung und flüssig umgesetzt (angezeigt) wird. Die klassische „Gameloop“, wie sie zum Beispiel in [1] beschrieben wird, erledigt alle Aufgaben des Spiels in einer Endlos-Schleife, die so schnell es die CPU erlaubt ausgeführt wird: vom Entgegennehmen der Benutzeraktionen bis zum Ausgeben der Grafik und des Sounds. Das „Spiel der Unterschrift“ lässt sich gut darauf abbilden, auch wenn es deutlich weniger Teilaufgaben enthält.
Spiele, die auf modernen Multi-Threaded-Architekturen laufen, bearbeiten ihre Teilaufgaben häufig in
voneinander unabhängigen Threads, um den Spielablauf flüssiger zu gestalten (siehe [1] und [2]). Genau das
ist auch beim „Spiel“ Unterschrift der Schlüssel zum Erfolg. Jede der drei Teilaufgaben für die Unterschrift wird in einen eigenen Thread verlagert (Bild 2). Benutzereingaben werden im Event-Thread des Systems entgegengenommen und die relevanten Informationen in Thread-sicheren Datenstrukturen gespeichert: Eine Unterschrift besteht aus einer (geringen) Anzahl von Linien mit je einem Anfangspunkt und (unter Umständen sehr vielen) weiteren Punkten. Immer wenn der Finger abgesetzt und neu aufgesetzt wird, beginnt eine neue Linie. Wichtig ist, dass in diesem Thread sonst nichts weiter passiert, weil unter iOS die Events maximal 60-mal pro Sekunde neu über die Position des Fingers auf dem Display informieren. Wenn der Event-Thread zu beschäftigt ist, gehen Events verloren, und das macht die Unterschrift eckig und das Verhalten wenig flüssig. Außerdem ist es sinnvoll, die Datenstruktur, in der die Punkte gespeichert werden, so zu wählen, dass sie für das Zeichnen der Linien (CGContextAddLines) direkt verwendet werden kann und nicht erst aufwendig umgespeichert werden muss. Dies würde CPU-Zeit benötigen, die bei diesem „Echtzeit-Abenteuer“ kostbar ist.

Im eigens für diesen Zweck gestarteten bgDraw-Thread werden die Eingaben verarbeitet: Ungefähr alle 20 Millisekunden wird der Bereich (CGRect) bestimmt, in dem seit dem letzten Aufruf neue Liniensegmente hinzugekommen sind, damit nicht jedes Mal alles neu gezeichnet werden muss. Anschließend wird das System veranlasst (setNeedsDisplayInRect:updatedRect), im Display das Neuzeichnen zu starten. Im Display-Thread werden in drawRect: die Liniensegmente gezeichnet, die sich innerhalb des zu zeichnendes Bereichs befinden. Dabei müssen auch „ältere“ Linien gezeichnet werden, die die aktuelle überkreuzen – Unterschriften können recht „verworren“ sein. Das Zusammenspiel dieser drei Threads funktioniert so gut, dass es auch auf dem alten iPhone 3G noch flüssig vonstatten geht und gute Unterschriften zaubert (Bild 3).

Unterschrift als Bitmap

Um die Unterschrift anschließend als PNG oder andere Bitmap weiterverarbeiten zu können, kann man einen einfachen Trick anwenden: Die Implementierung der drawRect bestimmt als Erstes den aktuellen GraphicsContext (UIGraphicsGetCurrentContext), und das eigentliche Zeichnen passiert in drawRect:OnContext: Zum Erzeugen einer Bitmap kann so nach CGBitmap- ContextCreate unter Benutzung der implementierten Methode direkt auf diesen BitmapContext gezeichnet werden. Zu beachten ist dabei, dass das Koordinatensystem umgekehrt verläuft und mithilfe von CGContextTranslate- CTM(…, 0, height) und CGContextScaleCTM(…, 1, -1) umgedreht werden muss. Praxiserprobt Der hier vorgestellte Algorithmus kommt derzeit in einer kostenlosen App für Vertragsmanagement für iPad und iPhone, mit der Kündigungsschreiben erstellt und unterschrieben werden können, zum Einsatz. Die App hat über 150.000 Nutzer; täglich werden Tausende Unterschriften damit erstellt. Unterschreiben auch Sie Ihre Dokumente mit dem iPad. [ms]

[1] Game Programming; http://en.wikipedia.org/wiki/Game_programming#Game_structure
[2] Multithreaded Game Engine Architectures; www.gamasutra.com/view/feature/1830/multithreaded_game_engine_.php

Originalartikel als PDF