Skip to content

Overview

Availablility

TMS iCL is a set of components for true native iOS application development and is available for Embarcadero Delphi XE11, C++Builder XE11 or newer releases.

Frameworks

Starting from version 1.2, when deploying to the device, you will encounter linker errors like the one below.

Frameworks1

Here is a list with frameworks that need to be added to the SDK manager in order to have the components build and link correctly. The sample demonstrates how to add the ImageIO framework.

Framework
AVKit (iOS 8 or later)
CoreMotion
ImageIO
LocalAuthentication (iOS 8 or later)
MapKit
MessageUI
MobileCoreServices
MultipeerConnectivity
Social
WebKit

When adding the SDK through the SDK manager you will notice that it already adds a subset of the available Frameworks in the iOS SDK such as the UIKit and the Foundation framework.

To fix the linker error, you will need to add a reference to the ImageIO framework in the SDK Manager. To add a new framework, right-click on your project and choose the iOS Device target. Right-click on the target and choose “Edit SDK” from the popup menu.

Frameworks2

After clicking the correct option in the popup menu, the SDK Manager will popup, showing you which SDK’s you have imported and which frameworks are available for each SDK.

Frameworks3

Scroll down to the “Frameworks” section for the current SDK you are compiling / linking with. Click inside the “Frameworks” section, for example on the UIKit framework entry, and click on the new button at the top right next to the list to add a new framework entry (path item).

Frameworks4

Enter the following information in the popup dialog

Path on remote machine

XE5: ”/System/Library/Frameworks”

XE6 and later: “$(SDKROOT)/System/Library/Frameworks”

Framework name: ImageIO

click on ok. The last step necessary for a correct linking is to update the local SDK directory with the new information. Click on “Update local file cache” at the bottom of the SDK manager:

Frameworks5

Targetting for iOS Device should now compile and link without any issues.

View hierarchy

The TMS iCL components can be dropped directly as a child of the main application form, but can also be used as a child of another TMS FMX Native UI control. Included in the set is a TMSFMXNativeUIView control that can be compared with a TPanel in VCL. The view is typically used as a container control that can hold other controls. This is demonstrated below with a small sample.

Drop a TMSFMXNativeUIView on the form and add a TMSFMXNativeUIButton control as child of the view.

At designtime. At runtime.
vh1 vh2

When setting the visible property of the TMSFMXNativeUIView to false, the button will also disappear. If we have a large area of controls and need to apply scrolling, the TMSFMXNativeUIScrollView can be used as a container for other controls. The view can be used as a container control and be linked to a TMSFMXNativeUITableView item’s DetailView property and be displayed when clicking on the item.

Deployment

At some point your application might have the need to access external files such as images and text files, or perhaps a database that needs to be accessed. When creating a new mobile application, clicking on the project tab and selecting deployment shows a windows where these files can be added.

Deployment1

The deployment window already contains files that are deployed along with your application such as the various application icons and launch images.

To add a new file, click on the add button which will popup a file open dialog.

Deployment2

Add the file by clicking open in the dialog. The file will be listed in the deployment window of your project and can be accessed from your application.

Deployment3

To access this file from your application, you need to get the root directory and apply the name of your file as listed in the deployment page. Note that the root directory is read-only, so you will be unable to write data to the file, such as text files. To gain write access to your file you need to copy the file to the documents directory. Listed below are some helper functions that allow you to access your file and access the root or documents directory.

Root Directory :

function GetRootDirectory: String;
begin
  Result := ExtractFilePath(ParamStr(0));
end;

Documents Directory (requires iOSApi.Foundation and iOSApi.UIKit unit) :

function GetDocumentsDirectory: String;
var
  paths: NSArray;
begin
  Result := '';
  paths := TNSArray.Wrap(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True));
  if paths.count > 0 then
    Result := String(TNSString.Wrap(paths.objectAtIndex(0)).UTF8String);
end;

Copy a File (requires iOSApi.Foundation and iOSApi.UIKit units):

procedure CopyFile(ASource, ADestination: String);
var
  fileManager: NSFileManager;
  error: NSError;
begin
    fileManager := TNSFileManager.Create;
    fileManager.copyItemAtPath(NSStr(ASource), NSStr(ADestination), Error);
end;

iOS Simulator vs Device

The TMS iCL package supports deployment to simulator and to a real device. The simulator can be helpful in debugging and creating your application without the need to go through the device each time your application is modified. The process of going through the deployment phase is slower when targeting a real device.

We, however, strongly suggest testing your application on a real device at regular intervals during application development, to make sure that the application behaves like it has been developed. There are known limitations and issues in the simulator in terms of look and feel, and it is important to make sure that these limitations do not occur on a real device since the device will be used in the final stage of development and deployment.

Resources

TMSFMXNativeUITableViewMail source

{********************************************************************}
{                                                                    }
{ written by TMS Software                                            }
{            copyright © 2014                                        }
{            Email : info@tmssoftware.com                            }
{            Web : http://www.tmssoftware.com                        }
{                                                                    }
{ The source code is given as is. The author is not responsible      }
{ for any possible damage done due to the use of this code.          }
{ The complete source code remains property of the author and may    }
{ not be distributed, published, given or sold in any form as such.  }
{ No parts of the source code can be included in any other component }
{ or application without written authorization of the author.        }
{********************************************************************}

unit FMX.TMSNativeUITableViewMail;

interface

uses
  Classes, FMX.TMSNativeUITableView, SysUtils
  {$IFDEF IOS}
  ,iOSApi.UIKit, iOSApi.Foundation
  {$ENDIF}
  ;

type
  TTMSFMXNativeUITableViewMailItem = class(TTMSFMXNativeUITableViewItem)
  private
    FDate: TDateTime;
    FTitle: String;
    FDescription: string;
    FSender: String;
    FUnread: Boolean;
    procedure SetUnread(const Value: Boolean);
  published
    property Sender: String read FSender write FSender;
    property Date: TDateTime read FDate write FDate;
    property Title: String read FTitle write FTitle;
    property Description: string read FDescription write FDescription;
    property Unread: Boolean read FUnread write SetUnread;
  end;

  TTMSFMXNativeUITableViewMailItems = class(TTMSFMXNativeUITableViewItems)
  public
    function CreateItemClass: TCollectionItemClass; override;
  end;

  TTMSFMXNativeUITableViewMailSection = class(TTMSFMXNativeUITableViewSection)
  public
    function CreateItems: TTMSFMXNativeUITableViewItems; override;
  end;

  TTMSFMXNativeUITableViewMailSections = class(TTMSFMXNativeUITableViewSections)
  public
    function CreateItemClass: TCollectionItemClass; override;
  end;

  [ComponentPlatformsAttribute(pidiOSSimulator or pidiOSDevice)]
  TTMSFMXNativeUITableViewMail = class(TTMSFMXNativeUITableView)
  public
    constructor Create(AOwner: TComponent); override;
    function CreateSections: TTMSFMXNativeUITableViewSections; override;
    function GetItemHeight(ASection, ARow: Integer): Single; override;
    function GetItemStyle(ASection, ARow: Integer): TTMSFMXNativeUITableViewItemStyle; override;
    {$IFDEF IOS}
    procedure DoItemCreateCell(Sender: TObject; var ACell: UITableViewCell; AItemStyle: TTMSFMXNativeUITableViewItemStyle; ASection, ARow: Integer); override;
    procedure DoItemCustomizeCell(Sender: TObject; ACell: UITableViewCell; AItemStyle: TTMSFMXNativeUITableViewItemStyle; ASection, ARow: Integer); override;
    {$ENDIF}
  end;


implementation

{ TTMSFMXNativeUITableViewMailItems }

function TTMSFMXNativeUITableViewMailItems.CreateItemClass: TCollectionItemClass;
begin
  Result := TTMSFMXNativeUITableViewMailItem;
end;

{ TTMSFMXNativeUITableViewMailSection }

function TTMSFMXNativeUITableViewMailSection.CreateItems: TTMSFMXNativeUITableViewItems;
begin
  Result := TTMSFMXNativeUITableViewMailItems.Create(OwnerTableView, Self);
end;

{ TTMSFMXNativeUITableViewMailSections }

function TTMSFMXNativeUITableViewMailSections.CreateItemClass: TCollectionItemClass;
begin
  Result := TTMSFMXNativeUITableViewMailSection;
end;

{ TTMSFMXNativeUITableViewMail }

constructor TTMSFMXNativeUITableViewMail.Create(AOwner: TComponent);
begin
  inherited;
  if (csDesigning in ComponentState) and not
    ((csReading in Owner.ComponentState) or (csLoading in Owner.ComponentState)) then
  begin
    Options.Header := 'Mail';
    Options.ToolBar := True;
  end;
end;

function TTMSFMXNativeUITableViewMail.CreateSections: TTMSFMXNativeUITableViewSections;
begin
  Result := TTMSFMXNativeUITableViewMailSections.Create(Self);
end;

{$IFDEF IOS}
procedure TTMSFMXNativeUITableViewMail.DoItemCreateCell(Sender: TObject;
  var ACell: UITableViewCell; AItemStyle: TTMSFMXNativeUITableViewItemStyle;
  ASection, ARow: Integer);
var
  title: UILabel;
  senderName: UILabel;
  description: UILabel;
  date: UILabel;
  r: NSRect;
begin
  r.origin.x := 5;
  r.origin.y := 5;
  r.size.width := ACell.frame.size.width - 100;
  r.size.height := 15;
  senderName := TUILabel.Wrap(TUILabel.Wrap(TUILabel.OCClass.alloc).initWithFrame(r));
  senderName.setFont(TUIFont.Wrap(TUIFont.OCClass.systemFontOfSize(12)));
  senderName.setTextColor(TUIColor.Wrap(TUIColor.OCClass.orangeColor));
  senderName.setHighlightedTextColor(TUIColor.Wrap(TUIColor.OCClass.whiteColor));
  ACell.contentView.addSubview(senderName);

  r.origin.x := Acell.frame.size.width - 100;
  r.origin.y := 5;
  r.size.width := 100;
  r.size.height := 15;
  date := TUILabel.Wrap(TUILabel.Wrap(TUILabel.OCClass.alloc).initWithFrame(r));
  date.setFont(TUIFont.Wrap(TUIFont.OCClass.boldSystemFontOfSize(12)));
  date.setTextAlignment(UITextAlignmentRight);
  date.setTextColor(TUIColor.Wrap(TUIColor.OCClass.blueColor));
  date.setHighlightedTextColor(TUIColor.Wrap(TUIColor.OCClass.whiteColor));
  ACell.contentView.addSubview(date);

  r.origin.x := 5;
  r.origin.y := senderName.frame.size.height + senderName.frame.origin.y;
  r.size.width := ACell.frame.size.width - 100;
  r.size.height := 25;
  title := TUILabel.Wrap(TUILabel.Wrap(TUILabel.OCClass.alloc).initWithFrame(r));
  title.setFont(TUIFont.Wrap(TUIFont.OCClass.boldSystemFontOfSize(16)));
  title.setHighlightedTextColor(TUIColor.Wrap(TUIColor.OCClass.whiteColor));
  ACell.contentView.addSubview(title);

  r.origin.x := 5;
  r.origin.y := title.frame.origin.y + title.frame.size.height;
  r.size.width := ACell.frame.size.width - 100;
  description := TUILabel.Wrap(TUILabel.Wrap(TUILabel.OCClass.alloc).initWithFrame(r));
  description.setHighlightedTextColor(TUIColor.Wrap(TUIColor.OCClass.whiteColor));
  ACell.contentView.addSubview(description);
end;

procedure TTMSFMXNativeUITableViewMail.DoItemCustomizeCell(Sender: TObject;
  ACell: UITableViewCell; AItemStyle: TTMSFMXNativeUITableViewItemStyle;
  ASection, ARow: Integer);
var
  title: UILabel;
  senderName: UILabel;
  description: UILabel;
  date: UILabel;
  it: TTMSFMXNativeUITableViewItem;
  mailit: TTMSFMXNativeUITableViewMailItem;
  str: NSString;
  r: NSRect;
begin
  senderName := TUILabel.Wrap(ACell.contentView.subviews.objectAtIndex(0));
  date := TUILabel.Wrap(ACell.contentView.subviews.objectAtIndex(1));
  title := TUILabel.Wrap(ACell.contentView.subviews.objectAtIndex(2));
  description := TUILabel.Wrap(ACell.contentView.subviews.objectAtIndex(3));

  it := GetItem(ASection, ARow);
  if Assigned(it) and (it is TTMSFMXNativeUITableViewMailItem) then
  begin
    mailit := it as TTMSFMXNativeUITableViewMailItem;
    if mailit.Unread then
    begin
      str := NSStr(ExtractFilePath(ParamStr(0)) + 'unread_mail.png');
      ACell.setImage(TUIImage.Wrap(TUIImage.OCClass.imageWithContentsOfFile(str)));
    end
    else
      ACell.setImage(nil);

    if Assigned(senderName) then
    begin
      senderName.setText(NSStr(mailit.Sender));
      r := senderName.frame;
      if Assigned(Acell.image) then
        r.origin.x := 15 + Acell.image.size.width
      else
        r.origin.x := 5;
      senderName.setFrame(r);
    end;
    if Assigned(title) then
    begin
      title.setText(NSStr(mailit.Title));
      r := title.frame;
      if Assigned(Acell.image) then
        r.origin.x := 15 + Acell.image.size.width
      else
        r.origin.x := 5;
      title.setFrame(r);
    end;
    if Assigned(description) then
    begin
      description.setText(NSStr(mailit.Description));
      r := description.frame;
      if Assigned(Acell.image) then
        r.origin.x := 15 + Acell.image.size.width
      else
        r.origin.x := 5;
      description.setFrame(r);
    end;
    if Assigned(date) then
    begin
      date.setText(NSStr(DateToStr(mailit.Date)));
    end;
  end;
end;
{$ENDIF}

function TTMSFMXNativeUITableViewMail.GetItemHeight(ASection,
  ARow: Integer): Single;
begin
  Result := 75;
end;

function TTMSFMXNativeUITableViewMail.GetItemStyle(ASection,
  ARow: Integer): TTMSFMXNativeUITableViewItemStyle;
begin
  Result := isTableViewCellStyleCustom;
end;

{ TTMSFMXNativeUITableViewMailItem }

procedure TTMSFMXNativeUITableViewMailItem.SetUnread(const Value: Boolean);
begin
  FUnread := Value;
  UpdateSectionAtRow(Section.Index, Index);
end;

end.