Dealing with swipe gestures
Someone asked me earlier about handling swipe gestures under Smart Pascal, so I thought I could deal with that in this post. I’m on sickleave with a bad back so I’m scribling this on my iPad (so heads up regarding typos). Cant say I’m to impressed with the Bluetooth keyboard, but it kinda works.
Describe a swipe
As luck would have it I already wrote a swipe controller way back in 2014, so I’ll be posting that here. But before we get into the code , let’s take a few seconds to think about what a swipe really is, because it’s not as simple as you may think.
Just stop and think about it: what is the difference between a normal touch-move and a swipe? Same thing right? Well yes and no (or kinda). What it all boils down to is time (!). So the difference between a normal touch-move operation and a swipe, is that a swipe happens very quickly.
Latency and ranges
The time it takes from your finger touches the display until your finger leaves, is the latency of the swipe (see code below). Depending on the device this latency can vary. On an iPad you can have a latency as low as 10, while on iPhones you generally end up with 35 or 40. Anything lower and the swipe wont register.

The Microsoft Phone swipe header
Next there are ranges. A swipe involves your finger moving quickly in some direction, and the controller has some default ranges for measuring these things. These ranges are simple minimum and maximum values for the distance. If you have hardly moved your finger then it’s not really a swipe; and if you move your finger slowly over a long distance, that’s not a swipe either (well, it can be. Just adjust the latency factor to suit your needs). At least not for components designed to be flicked back and forth.
The swipe controller class was actually written for the Microsoft Mobile component package (will be included with Smart Mobile Studio in the future), which implements the most popular MS controls used on Windows 10 touch devices. Fully GPU powered, animated, skinned and working brilliantly.
They also look good under iOS. Everyone is skinning their app’s these days, so mixing and matching between platforms is a great benefit.
Controller vs. inheritance
A controller class does not need to be inherited from. Instead, you create an instance and attach it to an already existing visual control. This is pretty cool because it doesn’t mess with your event handlers, it just extends your control over the existing behavior. Controllers are handy in situations where inheritance will be tricky, require to much refactoring or where the source is off-limits.
Using the controller is super simple:
FSwiper := TSwipeController.Create(nil); FSwiper.Attach(self); FSwiper.OnSwipe := procedure (sender:TObject;const Direction:TSwipeControllerDirection) begin showmessage("Swipe detected @ " + ord(direction).toString); end;
To detach the controller just call the Detach() method, or free the object. It will dispose of everything automatically. Note: If you pass along the control in the constructor, it attaches automatically.
The code
Well here is the code. Enjoy! Also remember that you can check this out from GitHub.
unit swipecontroller; interface uses system.types, system.dateutils, SmartCL.System, SmartCL.Components; type TSwipeControllerDirection = ( sdNone=0, sdLeft, sdRight, sdDown, sdUp ); TSwipeControllerEvent = Procedure (sender:TObject; const Direction:TSwipeControllerDirection); TSwipeControllerInfo = class public begins: TDateTime; sX: Integer; sY: Integer; eX: Integer; eY: Integer; end; TSwipeRange = class private FMin: Integer; FMax: Integer; protected procedure SetMin(Value:Integer);virtual; procedure SetMax(Value:Integer);virtual; public property Minimum:Integer read FMin write SetMin; property Maximum:Integer read FMax write SetMax; constructor Create(AMin,AMax:Integer); end; TSwipeController = class(TObject) private FAttached: Boolean; FControl: TW3TagObj; FHRange: TSwipeRange; FVRange: TSwipeRange; FInfo: TSwipeControllerInfo; FDirection: TSwipeControllerDirection; protected FTouchHandleStart: THandle; FTouchHandleMove: THandle; FTouchHandleUp: THandle; procedure SetupGestures; procedure RemoveGestures; public Property Latency:Integer; property HRange:TSwipeRange read FHRange; property VRange:TSwipeRange read FVRange; Property Owner:TW3TagObj read FControl; property Attached:Boolean read FAttached; Property OnSwipe:TSwipeControllerEvent; procedure Attach(const AOwner:TW3TagObj); procedure Detach; constructor Create(const AOwner:TW3TagObj);virtual; destructor Destroy;Override; end; implementation //############################################################################ // TSwipeRange //############################################################################ constructor TSwipeRange.Create(AMin,AMax:Integer); begin inherited Create; FMin:=AMin; FMax:=AMax; end; procedure TSwipeRange.SetMin(Value:Integer); begin FMin := TInteger.EnsureRange(Value,10,1000); end; procedure TSwipeRange.SetMax(Value:Integer); begin FMax := TInteger.EnsureRange(Value,10,1000); end; //############################################################################ // TSwipeController //############################################################################ constructor TSwipeController.Create(const AOwner:TW3TagObj); begin inherited Create; FHRange:=TSwipeRange.Create(20,40); // [min]---X---[max] FVRange:=TSwipeRange.Create(20,40); // [min]---Y---[max] FInfo := TSwipeControllerInfo.Create; Latency := 35; if assigned(AOwner) then Attach(Aowner); end; destructor TSwipeController.Destroy; begin if FAttached then Detach; FHRange.free; FVRange.free; FInfo.free; inherited; end; procedure TSwipeController.Attach(const AOwner:TW3TagObj); begin if FAttached then Detach; if AOwner<>NIl then begin FControl := AOwner; FAttached := true; FControl.Handle.readyExecute( procedure () begin SetupGestures; end); end; end; procedure TSwipeController.Detach; begin if FAttached then begin try RemoveGestures; finally FAttached := false; end; end; end; procedure TSwipeController.RemoveGestures; begin FControl.Handle.removeEventListener(FTouchHandleStart); FControl.Handle.removeEventListener(FTouchHandleMove); FControl.Handle.removeEventListener(FTouchHandleUp); FTouchHandleStart := unassigned; FTouchHandleMove := unassigned; FTouchHandleUp := unassigned; end; procedure TSwipeController.SetupGestures; begin FTouchHandleStart:=FControl.Handle.addEventListener ('touchstart', procedure (e:variant) begin e.preventDefault; var t := e.touches[0]; FInfo.sx:=t.screenX; FInfo.sy:=t.screenY; end); FTouchHandleMove:=FControl.Handle.addEventListener ('touchmove', procedure (e:variant) begin e.preventDefault; if (e.touches) then Begin if e.touches.length>0 then begin var t := e.touches[0]; FInfo.eX:=t.screenX; FInfo.eY:=t.screenY; FInfo.begins:=now; end; end; end); FTouchHandleUp:=FControl.Handle.addEventListener ('touchend', procedure (e:variant) var mTicks: Integer; begin e.preventDefault; FDirection:=sdNone; (* How many Ms since touch and release? *) mTicks:=MillisecondsBetween(FInfo.begins,now); if (mTicks <= Latency) then begin if (FInfo.ex - FHRange.Minimum > FInfo.sx) or (FInfo.ex + FHRange.Minimum < FInfo.sx) then begin if (FInfo.ey < (FInfo.sy + FVRange.Maximum)) and (FInfo.sy > (FInfo.ey - FVRange.Maximum)) then begin if (FInfo.ex > FInfo.sx) then FDirection:=sdRight else FDirection:=sdLeft; end; end; if ((FInfo.ey - FVRange.Minimum) > FInfo.sy) or ((FInfo.ey + FVRange.Minimum) < FInfo.sY) then begin if (FInfo.ex < (FInfo.sx + FHRange.Maximum)) and (FInfo.sx > (FInfo.ex - FHRange.Maximum)) then begin if FInfo.ey > FInfo.sY then FDirection :=sdDown else FDirection :=sdUp; end; end; if assigned(OnSwipe) then begin if FDirection<>sdNone then OnSwipe(self,FDirection); end; end; end); end; end.
Glad you’re back
Thnx buddy 🙂
The key to this element is that when swiping,
Hi, I’ve created a fast demo using SMS page transition,

the page follows your finger horizontally so as to give the user immediate feedback that he/she is swiping between pages.
..but I would like to implement page transition only when the swipe gesture has crossed a certain threshold. Any idea.
BTW, I have updated the system.animation.tween.pas unit add some easing functions
(41 functions) See at https://raw.githubusercontent.com/smartpascal/smartms/master/games/Easing/system.animation.tween.pas
If you mean like the iBooks page thing? So you get to move the page up to a “snap to” point — like a rubber band?
Well, you would need to use a parent container which grabs the input and moves the “page” accordingly. Then use some values to emulate resistance.
There is a JS library for this, could be worth porting (!) Dont remember the name right now, but google around and you will find it.
Also found this, which is quite cool:
http://www.html5rocks.com/static/demos/20things_pageflip/example/index.html
I’ll check out the fork on github!
Ah, saw the GIF now, sorry.
Well, the same rules would apply as i mention above. Control has to happen from the container control (just an extra TW3CustomControl). I pretty much the same with the DBGrid component where you can move the column-headers around. Then i use CSS transformation for GPU movement.
Its basically just:
1. Allow user to move while finger/mouse is down
2. if released before thresh-hold, use distance as speed factor and tween back to zero
3. if released beyond thresh-hold, use distance to end as speed-factor
For the background layer (new page being swiped into), you would need to play with the distance value and tween it to match. You will have to update the tween values during the whole operation (new target values). This would be very hard with CSS, but simple with TTween ..
For the swipe capture, use the class above (new version on git, just update the fork), and trigger an event which gives you distance between sx and dx (sourceX and destinationX). That should do it.