// ------------------------------------------------------------------------------
// DPF.iOS.UIImageView Component
//
// Dadeh Pardazane Faragir ( DPF ) Co.
//
// Web: http://www.dpfaragir.com
//
// Developed By: Babak Yaghoobi
//
// Email #1: yaghoobi@dpfaragir.com
// Email #2: b_yaghobi@yahoo.com
//
// ------------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ------------------------------------------------------------------------------
unit DPF.iOS.UIImageView;

interface

{$I DPF.iOS.Defs.inc}

uses
  System.SysUtils,
  System.Classes,
  System.Types,
  System.UITypes,
  System.Variants,
  System.DateUtils,
  System.Math,
  System.TypInfo,

  DPF.iOS.BaseControl,
  DPF.iOS.HTTP,
  DPF.iOS.UILabel,
  DPF.iOS.UITextView,
  DPF.iOS.NSOperationQueue,
  DPF.iOS.UIActivityIndicatorView,
  DPF.iOS.CacheManager,
  DPF.iOS.Classes,

{$IFDEF IOS}
  Posix.Unistd,
  Macapi.ObjectiveC,
  Macapi.ObjCRuntime,
  IOSapi.CocoaTypes,
  IOSapi.Foundation,
  IOSapi.Uikit,
  IOSapi.CoreGraphics,
  IOSapi.CoreImage,
  IOSapi.QuartzCore,
  FMX.Platform.iOS,
  DPF.iOS.Common,
{$ENDIF}
  FMX.Layouts, FMX.Memo,
{$IFDEF DELPHIXE5} FMX.Graphics, {$ENDIF}FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;

type

  TDPFImageView = class;

{$IFDEF IOS}

  // ------------------------------------------------------------------------------
  ImageViewGestureRecognizer = interface( IObjectiveC )
    ['{BBE3DCF5-1492-4DDC-A413-C87E78273457}']
    procedure handleSingleTap( gr: UITapGestureRecognizer ); cdecl;
  end;

  TDPFImageViewGestureRecognizerDelegate = class( TOCLocal, ImageViewGestureRecognizer )
  private
    FDPFImageView: TDPFImageView;
  public
    constructor Create( AImageView: TDPFImageView );
    procedure handleSingleTap( gr: UITapGestureRecognizer ); cdecl;
  end;
{$ENDIF}

  TDPFOnImageClicked           = procedure( Sender: TObject ) of object;
  TDPFOnBeforeAsyncImageLoaded = procedure( Sender: TObject ) of object;
  TDPFOnAsyncImageLoadError    = procedure( Sender: TObject; Error: string ) of object;
  TDPFImageOnProgress          = procedure( Sender: TObject; const DownloadSize: Int64; const DownloadedSize: Int64 ) of object;

  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPFImageView = class( TDPFiOSBaseControl )
  private
{$IFDEF IOS}
    FUIImageView                          : UIImageView;
    FIndicator                            : TDPFActivityIndicatorView;
    FTapGestureRecognizer                 : UITapGestureRecognizer;
    FDPFImageViewGestureRecognizerDelegate: TDPFImageViewGestureRecognizerDelegate;
{$ENDIF}
    FBackgroundColor     : TAlphaColor;
    FImageList           : TStrings;
    FAnimationDuration   : Double;
    FAnimationRepeatCount: Integer;
    FBitmap              : TBitMap;
    FLoadFromURL         : Boolean;
    FLoadAsync           : Boolean;
    FShowIndicator       : Boolean;
    FIndicatorStyle      : TDPFActivityIndicatorViewStyle;

    FNumberOfTouchesRequired : Byte;
    FNumberOfTapsRequired    : Byte;
    FDelaysTouchesBegan      : Boolean;
    FDelaysTouchesEnded      : Boolean;
    FOnClick                 : TDPFOnImageClicked;
    FCacheImage              : Boolean;
    FOperationQueue          : TDPFNSOperationQueue;
    FRetryCount              : Integer;
    FRetryLoading            : Integer;
    FOnAsyncImageLoaded      : TNotifyEvent;
    FOnAsyncImageLoadError   : TDPFOnAsyncImageLoadError;
    FOnBeforeAsyncImageLoaded: TDPFOnBeforeAsyncImageLoaded;
    FOnProgress              : TDPFImageOnProgress;
    FGrayScale               : Boolean;
    FIndicatorAlpha          : Single;
    FIndicatorBackgroundColor: TAlphaColor;
    FIndicatorColor          : TAlphaColor;
    procedure SetBackgroundColor( const Value: TAlphaColor );
    procedure SetImageList( const Value: TStrings );
    procedure SetBitmap( const Value: TBitMap );
    procedure SetOnClick( const Value: TDPFOnImageClicked );
    function GetIsLoadingImage: Boolean;
    function GetImageIsLoaded: Boolean;
{$IFDEF IOS}
    procedure LoadImageAsyncURL( URL: string );
    procedure ShowIndicatorView;
    procedure HideIndicatorView;
    function CheckCachedData( HTTPURL: string ): Boolean;
{$ENDIF}
  protected
    DPFHttp: TDPFHttp;
    procedure DoBitmapChanged( Sender: TObject );
    procedure Resize; override;
    procedure Move; override;
{$IFNDEF IOS}
    procedure Paint; override;
{$ELSE}
    procedure OnFinishAsyncLoading( Sender: TObject; Data: NSData; var isFree: Boolean );
    procedure OnErrorAsyncLoading( Sender: TObject; Error: string; var isFree: Boolean );
{$ENDIF}
  public
{$IFDEF IOS}
    function GetUIImage: UIImage;
    procedure Loaded; override;
    // procedure ResizeIOSImage( newX, newY, newWidth: Single; newHeight: Single );
{$ENDIF}
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
    procedure StopAnimating;
    procedure StartAnimating;
    procedure ClearImage;
    procedure UpdateBitmap;
{$IFDEF IOS}
    procedure ReloadImage;
    function GetColoredImage( Image: UIImage; Color: TAlphaColors ): UIImage;
    // function SetImageBlur( blur: Single ): UIImage;

    procedure SetImage( AImageView: TDPFImageView ); overload;
    procedure SetImage( Image: UIImage; AndRelease: Boolean = true ); overload;
    procedure SetImage( Image: TBitMap ); overload;
    procedure LoadFromFile( FileName: string );
    procedure SaveToFile( FileName: string );
    procedure CancelLoading;
{$ENDIF}
    property isLoadingImage: Boolean read GetIsLoadingImage;
    property isImageLoaded: Boolean read GetImageIsLoaded;
  published
    property BackgroundColor     : TAlphaColor read FBackgroundColor write SetBackgroundColor default TAlphaColors.Null;
    property ImageList           : TStrings read FImageList write SetImageList;
    property AnimationDuration   : Double read FAnimationDuration write FAnimationDuration;
    property AnimationRepeatCount: Integer read FAnimationRepeatCount write FAnimationRepeatCount default 0;
    property Bitmap              : TBitMap read FBitmap write SetBitmap;
    property LoadFromURL         : Boolean read FLoadFromURL write FLoadFromURL default false;
    property CacheImage          : Boolean read FCacheImage write FCacheImage default false;
    property LoadAsync           : Boolean read FLoadAsync write FLoadAsync default false;
    property OperationQueue      : TDPFNSOperationQueue read FOperationQueue write FOperationQueue;
    property RetryLoading        : Integer read FRetryLoading write FRetryLoading default 1;

    property ShowIndicator           : Boolean read FShowIndicator write FShowIndicator default false;
    property IndicatorStyle          : TDPFActivityIndicatorViewStyle read FIndicatorStyle write FIndicatorStyle default aisWhite;
    property IndicatorAlpha          : Single read FIndicatorAlpha write FIndicatorAlpha;
    property IndicatorColor          : TAlphaColor read FIndicatorColor write FIndicatorColor default TAlphaColors.White;
    property IndicatorBackgroundColor: TAlphaColor read FIndicatorBackgroundColor write FIndicatorBackgroundColor default TAlphaColors.Black;

    property NumberOfTapsRequired   : Byte read FNumberOfTapsRequired write FNumberOfTapsRequired default 1;
    property NumberOfTouchesRequired: Byte read FNumberOfTouchesRequired write FNumberOfTouchesRequired default 1;

    property DelaysTouchesBegan: Boolean read FDelaysTouchesBegan write FDelaysTouchesBegan default true;
    property DelaysTouchesEnded: Boolean read FDelaysTouchesEnded write FDelaysTouchesEnded default false;

    property GrayScale: Boolean read FGrayScale write FGrayScale default false;

    property OnClick                 : TDPFOnImageClicked read FOnClick write SetOnClick;
    property OnAsyncImageLoaded      : TNotifyEvent read FOnAsyncImageLoaded write FOnAsyncImageLoaded;
    property OnBeforeAsyncImageLoaded: TDPFOnBeforeAsyncImageLoaded read FOnBeforeAsyncImageLoaded write FOnBeforeAsyncImageLoaded;
    property OnAsyncImageLoadError   : TDPFOnAsyncImageLoadError read FOnAsyncImageLoadError write FOnAsyncImageLoadError;
    property OnProgress              : TDPFImageOnProgress read FOnProgress write FOnProgress;

    property AutoresizingMask;
    property UserInteraction;
    property ContentMode;
    property Alpha;
    property Align;
    property Position;
    property Width;
    property Height;
    property Enabled;
    property Visible;
  end;

  // ------------------------------------------------------------------------------

implementation

// ------------------------------------------------------------------------------
{ TDPFImageView }
// ------------------------------------------------------------------------------
constructor TDPFImageView.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );

  FGrayScale                := false;
  FOperationQueue           := nil;
  FImageList                := TStringList.Create;
  FBitmap                   := TBitMap.Create( 0, 0 );
  FBitmap.OnChange          := DoBitmapChanged;
  ControlCaption            := 'ImageView';
  FBackgroundColor          := TAlphaColors.Null;
  FAnimationDuration        := 1;
  FAnimationRepeatCount     := 0;
  FLoadFromURL              := false;
  FLoadAsync                := false;
  FShowIndicator            := false;
  FIndicatorStyle           := aisWhite;
  FIndicatorAlpha           := 0.7;
  FIndicatorBackgroundColor := TAlphaColors.Black;
  FIndicatorColor           := TAlphaColors.White;
  FCacheImage               := false;
  FRetryLoading             := 1;

  FNumberOfTapsRequired    := 1;
  FNumberOfTouchesRequired := 1;

  FDelaysTouchesBegan := true;
  DelaysTouchesEnded  := false;

{$IFDEF IOS}
  FUIImageView := TUIImageView.Wrap( TUIImageView.Alloc.initWithFrame( CGRectMake( 10, 10, 10, 10 ) ) );
  FUIControl   := FUIImageView;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
destructor TDPFImageView.Destroy;
begin
  FBitmap.DisposeOf;
  FImageList.DisposeOf;

{$IFDEF IOS}
  CancelLoading;
  HideIndicatorView;

  if assigned( FTapGestureRecognizer ) then
    FUIImageView.removeGestureRecognizer( FTapGestureRecognizer );

  if assigned( FDPFImageViewGestureRecognizerDelegate ) then
    FDPFImageViewGestureRecognizerDelegate.DisposeOf;

{$ENDIF}
  inherited;
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.DoBitmapChanged( Sender: TObject );
begin
{$IFDEF IOS}
  if Assigned( FUIImageView ) then
    FUIImageView.SetImage( BitmapToUIImage( FBitmap ) );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
function TDPFImageView.GetImageIsLoaded: Boolean;
begin
{$IFDEF IOS}
  result := Assigned( FUIImageView.image ) and ( FUIImageView.image.size.width > 0.99 );
{$ELSE}
  result := false;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
function TDPFImageView.GetIsLoadingImage: Boolean;
begin
{$IFDEF IOS}
  result := Assigned( FIndicator ) and FIndicator.isAnimating;
{$ELSE}
  result := false;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
{$IFDEF IOS}

procedure TDPFImageView.OnErrorAsyncLoading( Sender: TObject; Error: string; var isFree: Boolean );
begin
  CancelLoading;
  isFree := true;

  if FRetryCount < FRetryLoading then
  begin
    inc( FRetryCount );
    LoadImageAsyncURL( FImageList[0] );
  end
  else
  begin
    HideIndicatorView;
  end;
  if ( FRetryCount < FRetryLoading ) and Assigned( OnAsyncImageLoadError ) then
    OnAsyncImageLoadError( Self, Error );
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.OnFinishAsyncLoading( Sender: TObject; Data: NSData; var isFree: Boolean );
var
  ErrStr  : string;
  Img     : UIImage;
  NSResult: NSString;
begin
  HideIndicatorView;
  Img := TUIImage.Wrap( TUIImage.OCClass.imageWithData( Data ) );
  if Assigned( Img ) and ( Assigned( Img.CGImage ) or Assigned( Img.CIImage ) ) and ( Img.size.width > 0.99 ) and ( Img.size.height > 0.99 ) then
  begin
    SetImage( Img );
    if assigned( OnAsyncImageLoaded ) then
      OnAsyncImageLoaded( self );
    { if FCacheImage then
      begin
      HashFileName := GetHashFileName( FImageList[0], True );
      Data.writeToFile( NSStr( HashFileName ), True );
      end; }

    CancelLoading;
    isFree := true;

  end
  else
  begin
    ErrStr   := '';
    NSResult := nil;
    if Data.length < 10 * 1024 then
    begin
      NSResult := TNSString.Wrap( TNSString.Alloc.initWithData( Data, NSUTF8StringEncoding ) );
      ErrStr   := UTF8ToString( NSResult.UTF8String );
    end;
    // ShowMessage(ErrStr);
    DPFNSLog( 'TDPFImageView.OnFinishAsyncLoading: ' + ErrStr );
    OnErrorAsyncLoading( Self, 'Bad image data : ' + ErrStr, isFree );
    if Assigned( NSResult ) then
      NSResult.release;
  end;
end;

// ------------------------------------------------------------------------------
function TDPFImageView.GetUIImage: UIImage;
begin
  Result := FUIImageView.image;
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.HideIndicatorView;
begin
  if Assigned( FIndicator ) then
  begin
    FIndicator.StopAnimating;
    FIndicator.DisposeOf;
    FIndicator := nil;
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.ShowIndicatorView;
begin
  if not FShowIndicator then
    exit;

  if not Assigned( FIndicator ) then
  begin
    FIndicator        := TDPFActivityIndicatorView.Create( self );
    FIndicator.Parent := Self;
  end;
  FIndicator.SetBounds( 0, 0, Width, height );
  FIndicator.Alpha                  := FIndicatorAlpha;
  FIndicator.ActivityIndicatorStyle := IndicatorStyle;
  FIndicator.BackgroundColor        := FIndicatorBackgroundColor;
  FIndicator.Color                  := FIndicatorColor;
  FIndicator.Loaded;
  FIndicator.StartAnimating;

end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.LoadImageAsyncURL( URL: string );
begin
  ShowIndicatorView;
  if Assigned( OnBeforeAsyncImageLoaded ) then
    OnBeforeAsyncImageLoaded( Self );

  if not Assigned( DPFHttp ) then
  begin
    DPFHttp                  := TDPFHttp.Create( nil );
    DPFHttp.OnReceiveDataBin := OnFinishAsyncLoading;
    DPFHttp.OnReceiveError   := OnErrorAsyncLoading;
    DPFHttp.OperationQueue   := FOperationQueue;
    DPFHttp.OnProgress       := FOnProgress;
  end;
  // DPFHttp.SetImageAsync( URL, FUIImageView );
  DPFHttp.GetUrlContentData( URL, [], True );
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFImageView.ClearImage;
begin
{$IFDEF IOS}
  if Assigned( DPFHttp ) then
    DPFHttp.ConnectionCancel;

  FUIImageView.setImage( nil );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
{$IFDEF IOS}

procedure TDPFImageView.Loaded;
begin
  DPFNSLog( 'TDPFImageView.Loaded: ' + name );

  FUIImageView := UIImageView( FUIControl );

  FUIImageView.setFrame( CGRectMake( Position.X, Position.Y, Width * Scale.X, Height * Scale.Y ) );
  FUIImageView.setHidden( not Visible );

  if not FBitmap.IsEmpty then
    DoBitmapChanged( Self )
  else
    SetImageList( nil );

  SetBackgroundColor( FBackgroundColor );

  addSubview( Self, ParentControl );
  // ----------------------------
  // Important
  inherited;
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.SaveToFile( FileName: string );
var
  Bmp: TBitMap;
begin
  Bmp := UIImageToBitmap( FUIImageView.Image, 0 );
  if Bmp <> nil then
  begin
    Bmp.SaveToFile( FileName );
    Bmp.DisposeOf;
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.SetImage( AImageView: TDPFImageView );
begin
  SetImage( AImageView.FUIImageView.image );
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.SetImage( Image: UIImage; AndRelease: Boolean = true );
begin
  if FGrayScale then
    Image := convertImageToGrayScale( Image );
  FUIImageView.SetImage( Image );
  if AndRelease then
    Image.release;
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.SetImage( Image: TBitMap );
begin
  SetImage( BitmapToUIImage( Image ) );
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.LoadFromFile( FileName: string );
var
  Bmp: TBitMap;
begin
  if not FileExists( FileName ) then
    exit;
  Bmp := TBitMap.CreateFromFile( FileName );
  if Bmp <> nil then
    SetImage( Bmp );
end;

// ------------------------------------------------------------------------------
(* function TDPFImageView.SetImageBlur( blur: Single ): UIImage;
  var
  inputImage : CIImage;
  filter    : CIFilter;
  NSA        : NSMutableArray;
  outputImage: CIImage;
  outImage   : CGImageRef;
  context    : CIContext;
  begin

  inputImage := TCIImage.Wrap( TCIImage.OCClass.imageWithCGImage( FUIImageView.image.CGImage ) );

  NSA := TNSMutableArray.Create;

  NSA.addObject( ( kCIInputImageKey as ILocalObject ).GetObjectID );
  NSA.addObject( ( inputImage as ILocalObject ).GetObjectID );

  NSA.addObject( ( NSStr( 'inputRadius' ) as ILocalObject ).GetObjectID );
  NSA.addObject( ( TNSNumber.Wrap( TNSNumber.OCClass.numberWithDouble( blur ) ) as ILocalObject ).GetObjectID );

  filter := TCIFilter.Wrap( TCIFilter.OCClass.filterWithName( NSStr( 'CIGaussianBlur' ), ( NSA as ILocalObject ).GetObjectID  ));

  outputImage := filter.outputImage;
  context     := TCIContext.Wrap( TCIContext.OCClass.contextWithOptions( nil ) );

  outImage := context.createCGImage( outputImage, outputImage.extent );

  result := TUIImage.Wrap( TUIImage.OCClass.imageWithCGImage( outImage ) );

  end; *)

// ------------------------------------------------------------------------------
function TDPFImageView.GetColoredImage( Image: UIImage; Color: TAlphaColors ): UIImage;
var
  context: CGContextRef;
  clr    : UIColor;
  rect   : CGRect;
begin

  // begin a new image context, to draw our colored image onto
  UIGraphicsBeginImageContext( Image.size );

  // get a reference to that context we created
  context := UIGraphicsGetCurrentContext( );

  // set the fill color
  clr := TColorToUIColor( FBackgroundColor );
  clr.setFill;

  // translate/flip the graphics context (for transforming from CG* coords to UI* coords
  CGContextTranslateCTM( context, 0, Image.size.height );
  CGContextScaleCTM( context, 1.0, -1.0 );

  // set the blend mode to color burn, and the original image
  CGContextSetBlendMode( context, kCGBlendModeColorBurn );
  rect := CGRectMake( 0, 0, Image.size.width, Image.size.height );
  CGContextDrawImage( context, rect, Image.CGImage );

  // set a mask that matches the shape of the image, then draw (color burn) a colored rectangle
  CGContextClipToMask( context, rect, Image.CGImage );
  CGContextAddRect( context, rect );
  CGContextDrawPath( context, kCGPathFill );

  // generate a new UIImage from the graphics context we drew onto
  result := TUIImage.Wrap( UIGraphicsGetImageFromCurrentImageContext( ) );
  UIGraphicsEndImageContext( );
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.CancelLoading;
begin
  if Assigned( DPFHttp ) then
  begin
    DPFHttp.OnReceiveDataBin := nil;
    DPFHttp.OnReceiveError   := nil;
    DPFHttp.OnProgress       := nil;
    DPFHttp.DisposeOf;
    DPFHttp := nil;
  end;
  HideIndicatorView;
end;

{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFImageView.Resize;
begin
  inherited;
{$IFDEF IOS}
  if Assigned( FUIImageView ) then
  begin
    // FUIImageView.setFrame( CGRectMake( Position.X, Position.Y, Width*Scale.X, Height*Scale.Y ) );
    if FShowIndicator and assigned( FIndicator ) then
      FIndicator.SetBounds( 0, 0, Width * Scale.X, Height * Scale.Y );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.Move;
begin
  Resize;
end;

// ------------------------------------------------------------------------------
{$IFNDEF IOS}

procedure TDPFImageView.Paint;
begin
  InternalPaint( '', TAlphaColors.Null, TDPFTextAlignment.taCenter, FBackgroundColor );
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFImageView.SetBackgroundColor( const Value: TAlphaColor );
begin
  FBackgroundColor := Value;
{$IFDEF IOS}
  if Assigned( FUIImageView ) then
  begin
    if FBackgroundColor <> TAlphaColors.Null then
      FUIImageView.SetBackgroundColor( TColorToUIColor( FBackgroundColor ) )
    else
      FUIImageView.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.UpdateBitmap;
begin
{$IFDEF IOS}
  FBitmap := UIImageToBitmap( FUIImageView.Image, 0 );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.SetBitmap( const Value: TBitMap );
begin
  FBitmap.Assign( Value );
end;

// ------------------------------------------------------------------------------
{$IFDEF IOS}
// ------------------------------------------------------------------------------
{ procedure TDPFImageView.ResizeIOSImage( newX, newY, newWidth: Single; newHeight: Single );
  begin
  FUIImageView.setFrame( CGRectMake( newX, newY, newWidth, newHeight ) );
  end; }

// ------------------------------------------------------------------------------
function TDPFImageView.CheckCachedData( HTTPURL: string ): Boolean;
var
  NSD   : NSData;
  Img   : UIImage;
  isFree: Boolean;
begin
  NSD    := GetCachedData( HTTPURL );
  result := NSD <> nil;
  if result then
  begin
    TThread.Queue( nil,
      procedure
      begin
        Img := TUIImage.Wrap( TUIImage.OCClass.imageWithData( NSD ) );
        if Assigned( Img ) and ( Assigned( Img.CGImage ) or Assigned( Img.CIImage ) ) and ( Img.size.width > 0.99 ) and ( Img.size.height > 0.99 ) then
        begin
          SetImage( Img );
          Img := nil;
          if assigned( OnAsyncImageLoaded ) then
            OnAsyncImageLoaded( self );
        end
        else
        begin
          OnErrorAsyncLoading( Self, '', isFree );
        end
      end );
  end
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.ReloadImage;
begin
  CancelLoading;
  SetImageList( nil );
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFImageView.SetImageList( const Value: TStrings );
{$IFDEF IOS}
var
  Img     : UIImage;
  I       : Integer;
  ImgArray: NSMutableArray;
  // HashFileName: string;
  NData : NSData;
  isFree: Boolean;
{$ENDIF}
begin
  FRetryCount := 0;
  if Assigned( Value ) then
    FImageList.Assign( Value );
{$IFDEF IOS}
  if FImageList.Count > 0 then
  begin
    if FImageList.Count = 1 then
    begin
      if FCacheImage then
        if checkCachedData( FImageList[0] ) then
          exit;

      (*
        if FCacheImage then
        begin
        HashFileName := GetHashFileName( FImageList[0], False );
        if FileExists( HashFileName ) then
        begin
        if FLoadAsync then
        begin
        Flag := true;
        TThread.Queue( nil,
        procedure
        begin
        try
        Img := TUIImage.Wrap( TUIImage.OCClass.imageWithContentsOfFile( NSStr( HashFileName ) ) );
        Flag := Assigned( Img ) and ( Assigned( Img.CGImage ) or Assigned( Img.CIImage ) ) and ( Img.size.width > 0.99 ) and ( Img.size.height > 0.99 );
        if Flag then
        begin
        setImage( Img );
        Img := nil;
        exit;
        end;
        except
        end;
        end );
        if Flag then
        exit;
        end
        else
        begin
        Img := TUIImage.Wrap( TUIImage.OCClass.imageWithContentsOfFile( NSStr( HashFileName ) ) );
        end;

        if Assigned( Img ) and ( Assigned( Img.CGImage ) or Assigned( Img.CIImage ) ) and ( Img.size.width > 0.99 ) and ( Img.size.height > 0.99 ) then
        begin
        SetImage( Img );
        Img := nil;
        exit;
        end
        else
        DeleteFile( HashFileName );
        end;
        end;
      *)

      if FLoadFromURL and FLoadAsync then
      begin
        ShowIndicatorView;
        LoadImageAsyncURL( FImageList[0] );
      end
      else if not FLoadFromURL and FLoadAsync then
      begin
        TThread.Queue( nil,
          procedure
          begin
            try
              Img := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FImageList[0] ) ) );
              if Assigned( Img ) and ( Assigned( Img.CGImage ) or Assigned( Img.CIImage ) ) and ( Img.size.width > 0.99 ) and ( Img.size.height > 0.99 ) then
              begin
                setImage( Img );
                Img := nil;
              end;
            except
            end;
          end );
      end
      else if not FLoadAsync then
      begin
        if not FLoadFromURL then
        begin
          Img := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FImageList[0] ) ) );
        end
        else if FLoadFromURL then
        begin
          NData  := TNSData.Wrap( TNSData.OCClass.dataWithContentsOfURL( TNSURL.Wrap( TNSURL.OCClass.URLWithString( NSStr( FImageList[0] ) ) ) ) );
          Img    := TUIImage.Wrap( TUIImage.OCClass.imageWithData( NData ) );
          isFree := false;
          OnFinishAsyncLoading( Self, NData, isFree );
        end;
        if Assigned( Img ) and ( Assigned( Img.CGImage ) or Assigned( Img.CIImage ) ) and ( Img.size.width > 0.99 ) and ( Img.size.height > 0.99 ) then
        begin
          SetImage( Img );
          Img := nil;
        end
      end;
    end
    else if FImageList.Count > 1 then
    begin
      FUIImageView.StopAnimating;
      ImgArray := TNSMutableArray.Wrap( TNSMutableArray.OCClass.arrayWithCapacity( FImageList.Count ) );

      for I := 0 to FImageList.Count - 1 do
      begin
        Img := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FImageList[I] ) ) );
        ImgArray.addObject( ( Img as ILocalObject ).GetObjectID );
      end;

      FUIImageView.setAnimationImages( ImgArray );
      FUIImageView.SetAnimationDuration( FAnimationDuration );
      FUIImageView.SetAnimationRepeatCount( FAnimationRepeatCount );
      FUIImageView.StartAnimating;
    end;
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.SetOnClick( const Value: TDPFOnImageClicked );
begin
  FOnClick := Value;

{$IFDEF IOS}
  if assigned( FTapGestureRecognizer ) then
    FUIImageView.removeGestureRecognizer( FTapGestureRecognizer );

  if assigned( FDPFImageViewGestureRecognizerDelegate ) then
    FDPFImageViewGestureRecognizerDelegate.DisposeOf;

  if Assigned( Value ) then
  begin
    FDPFImageViewGestureRecognizerDelegate := TDPFImageViewGestureRecognizerDelegate.Create( Self );
    FTapGestureRecognizer                  := TUITapGestureRecognizer.Wrap( TUITapGestureRecognizer.Alloc.initWithTarget( FDPFImageViewGestureRecognizerDelegate.GetObjectID, Sel_getUid( 'handleSingleTap:' ) ) );

    FTapGestureRecognizer.setNumberOfTapsRequired( FNumberOfTapsRequired );
    FTapGestureRecognizer.setNumberOfTouchesRequired( FNumberOfTouchesRequired );
    FTapGestureRecognizer.setDelaysTouchesBegan( FDelaysTouchesBegan );
    FTapGestureRecognizer.setDelaysTouchesEnded( FDelaysTouchesEnded );
    FUIImageView.removeGestureRecognizer( FTapGestureRecognizer );
    FUIImageView.addGestureRecognizer( FTapGestureRecognizer );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.StartAnimating;
begin
{$IFDEF IOS}
  FUIImageView.StartAnimating;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFImageView.StopAnimating;
begin
{$IFDEF IOS}
  FUIImageView.StopAnimating;
{$ENDIF}
end;

{$IFDEF IOS}
// ------------------------------------------------------------------------------
{ TDPFImageViewGestureRecognizerDelegate }

constructor TDPFImageViewGestureRecognizerDelegate.Create( AImageView: TDPFImageView );
begin
  inherited Create;
  FDPFImageView := AImageView;
end;

// ------------------------------------------------------------------------------
procedure TDPFImageViewGestureRecognizerDelegate.handleSingleTap( gr: UITapGestureRecognizer );
begin
  if Assigned( FDPFImageView.FOnClick ) then
    FDPFImageView.FOnClick( FDPFImageView );
end;

// ------------------------------------------------------------------------------
{$ENDIF}

initialization

end.
