UPDATE: If you’ve applied Update 1 for XE5, please revert any changes made based on this article, or ignore it if you have not made any changes. The issue has been resolved in Update 1 for the DEVICE; sadly, Update 1 causes an issue with  the Done bar in the simulator. Please refer to this article if you have installed Update 1.

If you’ve recently compiled your iOS app for iOS7 with Delphi XE5, and your app uses the virtual keyboard, you would have noticed that the “Done” bar is out of vertical alignment:

After a fair amount of research, I’m still a bit fuzzy on why this should be, however it appears to be to do with how iOS7 considers the total screen area. In any event, I’ve come up with a solution that at least for now appears to work.

The solution involves copying the FMX.VirtualKeyboard.iOS unit into your project directory, and making some changes to it. Firstly, the TCocoaVirtualKeyboardService needs a reference to the area occupied by the virtual keyboard when it is shown, so add the code indicated by the comment in the code below: (note that these are fragments of the code in the file. I am publishing only as much as to demonstrate what changes need to be made)

  TCocoaVirtualKeyboardService = class(TInterfacedObject,
    IFMXVirtualKeyboardService,
    IFMXVirtualKeyboardToolbarService)
  private
    FKeyboardHandler: TKeyboardEventHandler;
    { IFMXVirtualKeyboardToolbarService }
    FToolbarVisible: Boolean;
    FToolBar: UIToolBar;
    FFlexibleSepararator: UIBarButtonItem;
    FHideButton: UIBarButtonItem;
    FButtons: TList<TVirtualKeyboardToolButton>;
    FUpdatingButtons: Boolean;
    FToolbarEnabled: Boolean;
    FHideButtonVisible: Boolean;
    FStoredActiveForm: TComponent;
    // Add the following line:
    FVKRect: TRect;
    procedure SetToolbarVisible(const Value: Boolean);

Next, in the TKeyboardEventHandler.KeyboardWillShow method make the changes indicated:

procedure TKeyboardEventHandler.KeyboardWillShow(notification: Pointer);
var
  VKRect: TRect;
begin
  VKRect := GetKeyboardRect(notification);
  // Add the following line:
  CocoaKeyboardService.FVKRect := VKRect;
  CocoaKeyboardService.CreateToolbar;

Finally, in the TCocoaVirtualKeyboardService.GetToolbarFrame method:

function TCocoaVirtualKeyboardService.GetToolbarFrame: NSRect;
var
  ScreenRect: NSRect;
  // Add the following variables:
  App: UIApplication;
  Device: UIDevice;
  Offset: Single;
begin
  // Add the following lines:
  Offset := 0;
  App := TUIApplication.wrap(TUIApplication.OCClass.SharedApplication);
  {$IFDEF CPUARM}
  Device := TUIDevice.Wrap(TUIDevice.OCClass.currentDevice);
  if Pos('7', Device.systemVersion.UTF8String) = 1 then
    Offset := App.statusBarFrame.size.height;
  {$ENDIF}
  // Up to here
  ScreenRect := TUIScreen.Wrap(TUIScreen.OCClass.mainScreen).applicationFrame;

and:

    UIDeviceOrientationPortrait,
    UIDeviceOrientationPortraitUpsideDown:
      begin
        if ToolbarVisible then
          // Add the following line, and comment out the others as shown:
          Result.origin.y := ScreenRect.size.height - (FVKRect.Height + TOOLBAR_HEIGHT - Offset)
{
          if TUIDevice.Wrap(TUIDevice.OCClass.currentDevice).userInterfaceIdiom = UIUserInterfaceIdiomPhone then
            Result.origin.y := ScreenRect.size.height - 260
          else
            Result.origin.y := ScreenRect.size.height - 308
}
        else
          Result.origin.y := ScreenRect.size.height + Offset; // <-- Alter this line

and:

    UIDeviceOrientationLandscapeLeft,
    UIDeviceOrientationLandscapeRight:
      begin
        if ToolbarVisible then
          // Add the following line, and comment out the others as shown:
          Result.origin.y := ScreenRect.size.height - (FVKRect.Height + TOOLBAR_HEIGHT - Offset)
{
          if TUIDevice.Wrap(TUIDevice.OCClass.currentDevice).userInterfaceIdiom = UIUserInterfaceIdiomPhone then
            Result.origin.y := ScreenRect.size.width - 206
          else
            Result.origin.y := ScreenRect.size.width - 396
}
        else
          Result.origin.y := ScreenRect.size.width + Offset; // <-- Alter this line

These changes do a couple of things: they correct for the hardcoded screen size values used in GetToolbarFrame, and use a “hack” to account for the “Done” bar being out of place on the device, offsetting it by the size of the status bar.

I hope this modification goes some way to helping out others with the same issue that I had.