Fixing gesture “lag” near the screen edge on iOS
Dave Nottage - Feb/2020
Dave Nottage - Feb/2020
[SHOWTOGROUPS=4,20]
An enquiry I had from a Hans Jakobsen of EarMaster (brilliant music teaching software for PC, Mac, iPad and iPhone) recently was regarding an apparent “lag” for the OnMouseDown event for controls near the edge of the screen on devices with later versions of iOS.
This article poses a workaround for the problem.
A description of the problem
Hans had already done some research about the issue, and came across Для просмотра ссылки Войдиили Зарегистрируйся, and he asked me to look into how to solve it in Delphi.
Implementing preferredScreenEdgesDeferringSystemGestures
Firstly, I needed to find exactly where to implement the changes. The article says that in order to indicate which edges should take “precedence” (over system gestures) in your app, the preferredScreenEdgesDeferringSystemGestures method needs to be “overridden” in the class that implements UIViewController. In the case of Для просмотра ссылки Войдиили Зарегистрируйся, this is TFMXViewController in the FMX.Platform.iOS unit. Fortunately, this class is declared in the implementation section, so the unit can be “patched” without having to recompile any other units. It was then a simple matter of adding the method to the FMXViewController interface and implementing it in TFMXViewController (see changes below).
Telling iOS which edges to prefer
Next up was how to determine what edges are “preferred”, so I used an old trick of defining a property that could be set outside of FMX.Platform.iOS, which that unit could then use. The DW.ScreenEdgeManager unit declares a record called TScreenEdgeManager, which has a private field FPreferredScreenEdges which is a set of the desired “preferred” edges. The private field value is set inside the SetPreferredScreenEdges method, and that method ensures that the changes are flagged in the system by calling setNeedsUpdateOfScreenEdgesDeferringSystemGestures. This indicates to iOS that the next time a system gesture happens, it will need to call preferredScreenEdgesDeferringSystemGestures on the view controller to discover which edges apply.
The setNeedsUpdateOfScreenEdgesDeferringSystemGestures (as per the article I linked to) method was added in iOS 11, and since Delphi has a bit of catching up to do with iOS API changes, a bit of “hackery” was needed to call this method as can be seen by the declaration of UIViewControllerEx and TUIViewControllerEx, which essentially “extends” UIViewController.
The demo
I’ve Для просмотра ссылки Войдиили Зарегистрируйся (on GitHub) that uses the patch for FMX.Platform.iOS and the DW.ScreenEdgeManager unit to demonstrate how this works, and the difference between when there’s no “preferred” edges and where they are. Instead of creating a patch file this time, I’m detailing the changes you need to make. Copy FMX.Platform.iOS from the Delphi source\fmx folder into the demo project folder, and:
Add DW.ScreenEdgeManager to the implementation uses clause
Add the preferredScreenEdgesDeferringSystemGestures method to FMXViewController and TFMXViewController
Add the implementation of TFMXViewController.preferredScreenEdgesDeferringSystemGestures
To see the effect of the changes, run the app on a device which (for example) has a “swipe” bar at the bottom, and tap and hold the button at the point where the swipe bar is.
It might take a couple of tries, however you will notice that most of the time, there is a noticeable delay between when the button is tapped and the rectangle changes color.
Now check the “Fix lag” checkbox and repeat the earlier steps.
The rectangle changes color immediately, every time.
[/SHOWTOGROUPS]
An enquiry I had from a Hans Jakobsen of EarMaster (brilliant music teaching software for PC, Mac, iPad and iPhone) recently was regarding an apparent “lag” for the OnMouseDown event for controls near the edge of the screen on devices with later versions of iOS.
This article poses a workaround for the problem.
A description of the problem
Hans had already done some research about the issue, and came across Для просмотра ссылки Войди
Implementing preferredScreenEdgesDeferringSystemGestures
Firstly, I needed to find exactly where to implement the changes. The article says that in order to indicate which edges should take “precedence” (over system gestures) in your app, the preferredScreenEdgesDeferringSystemGestures method needs to be “overridden” in the class that implements UIViewController. In the case of Для просмотра ссылки Войди
Telling iOS which edges to prefer
Next up was how to determine what edges are “preferred”, so I used an old trick of defining a property that could be set outside of FMX.Platform.iOS, which that unit could then use. The DW.ScreenEdgeManager unit declares a record called TScreenEdgeManager, which has a private field FPreferredScreenEdges which is a set of the desired “preferred” edges. The private field value is set inside the SetPreferredScreenEdges method, and that method ensures that the changes are flagged in the system by calling setNeedsUpdateOfScreenEdgesDeferringSystemGestures. This indicates to iOS that the next time a system gesture happens, it will need to call preferredScreenEdgesDeferringSystemGestures on the view controller to discover which edges apply.
The setNeedsUpdateOfScreenEdgesDeferringSystemGestures (as per the article I linked to) method was added in iOS 11, and since Delphi has a bit of catching up to do with iOS API changes, a bit of “hackery” was needed to call this method as can be seen by the declaration of UIViewControllerEx and TUIViewControllerEx, which essentially “extends” UIViewController.
The demo
I’ve Для просмотра ссылки Войди
Add DW.ScreenEdgeManager to the implementation uses clause
uses System.Classes, System.SysUtils, System.Types, System.UITypes, System.TypInfo, System.Messaging, System.RTLConsts, System.Math, Macapi.ObjCRuntime, Macapi.CoreFoundation, Macapi.Helpers, iOSapi.CocoaTypes, iOSapi.Foundation, iOSapi.CoreGraphics, iOSapi.Helpers, FMX.Graphics, FMX.Consts, FMX.Controls, FMX.Canvas.GPU, FMX.TextLayout, FMX.Text, FMX.Styles, FMX.Gestures, FMX.Context.GLES, FMX.Forms3D, FMX.Utils, FMX.Graphics.iOS, FMX.Context.GLES.iOS, FMX.Controls.iOS, FMX.Gestures.iOS, FMX.Helpers.iOS, FMX.Dialogs.iOS, FMX.Platform, FMX.Platform.Timer.iOS, FMX.Platform.SaveState.iOS, FMX.MultiTouch.iOS, FMX.Platform.Metrics.iOS, FMX.Platform.Device.iOS, FMX.Platform.Screen.iOS, FMX.Platform.Logger.iOS, DW.ScreenEdgeManager; |
Add the preferredScreenEdgesDeferringSystemGestures method to FMXViewController and TFMXViewController
FMXViewController = interface(UIViewController) ['{FB1283E6-B1AB-419F-B331-160096B10C62}'] // Some code snipped here function preferredScreenEdgesDeferringSystemGestures: UIRectEdge; cdecl; // <------ end; TFMXViewController = class(TOCLocal) // Some code snipped here function preferredScreenEdgesDeferringSystemGestures: UIRectEdge; cdecl; // <------ public |
Add the implementation of TFMXViewController.preferredScreenEdgesDeferringSystemGestures
function TFMXViewController.preferredScreenEdgesDeferringSystemGestures: UIRectEdge; const UIRectEdges: array[TScreenEdge] of UIRectEdge = (UIRectEdgeTop, UIRectEdgeLeft, UIRectEdgeBottom, UIRectEdgeRight); var LScreenEdge: TScreenEdge; begin Result := UIRectEdgeNone; for LScreenEdge := Low(TScreenEdge) to High(TScreenEdge) do begin if LScreenEdge in TScreenEdgeManager.PreferredScreenEdges then Result := Result or UIRectEdges[LScreenEdge]; end; end; |
To see the effect of the changes, run the app on a device which (for example) has a “swipe” bar at the bottom, and tap and hold the button at the point where the swipe bar is.
It might take a couple of tries, however you will notice that most of the time, there is a noticeable delay between when the button is tapped and the rectangle changes color.
Now check the “Fix lag” checkbox and repeat the earlier steps.
The rectangle changes color immediately, every time.
[/SHOWTOGROUPS]