Skip to content

Xamarin.Forms Advanced

Daniel Luberda edited this page May 6, 2019 · 32 revisions

IImageViewHandler / IImageSourceHandler

You can now easily use FFImageLoading with standard views like Xamarin.Forms.Image. Just call this after Xamarin.Forms.Init call:

  • CachedImageRenderer.InitImageViewHandler() on Android
  • CachedImageRenderer.InitImageSourceHandler() on iOS / Mac and others

Initial idea here: https://github.com/roubachof/Xamarin.Forms.ImageSourceHandlers (thanks to @roubachof)

Usage in ListView with a lot of elements and ListViewCachingStrategy.RecycleElement enabled (default)

// EDIT Since XF 3.0 you could also use BindingMode.OneTime to have similar effect.

If you use CachedImage views inside ViewCells used by ListViews with ListViewCachingStrategy.RecycleElement enabled, don't rely on bindings for setting the Source property (they are so slow!) - please use ViewCell's OnBindingContextChanged override of a ViewCell implementation to update it, avoiding loading images to wrong views. Example:

public class MyCustomCell : ViewCell
{
	readonly CachedImage cachedImage = null;

	public MyCustomCell()
	{
		cachedImage = new CachedImage();
		View = cachedImage;
	}

	protected override void OnBindingContextChanged()
	{
		// you can also put cachedImage.Source = null; here to prevent showing old images occasionally
		cachedImage.Source = null;
		var item = BindingContext as Item;

		if (item == null)
		{
			return;
		}

		cachedImage.Source = item.ImageUrl;

		base.OnBindingContextChanged();
	}
}

More info here: https://developer.xamarin.com/guides/xamarin-forms/user-interface/listview/performance/#RecycleElement

Using ImageSource.FromStream (StreamImageSource):

  • Use custom cache key factory + custom StreamImageSource subclass which has a Key property. This will allow FFImageLoading to use memory cache properly, without it, no images would be reused.
  • If your source is a byte array, reuse the same array: ImageSource.FromStream(async (token) => new MemoryStream(byteArrayWhichIsReusedMultipleTimes))

Here's the sample of using custom cache key factory + custom StreamImageSource subclass which has a Key property sample1 sample2. This will allow FFImageLoading to use memory cache properly, without it, no images would be reused.

Using ImageSource.FromResource method

It's better to use EmbeddedResourceImageSource which has a better performance and lets you to avoid a lot of issues.

If you use ImageSource.FromResource("ProjectName.Resources.image.png"); PLEASE also set a custom cache key. See this: https://github.com/luberda-molinet/FFImageLoading/issues/696

ImageService.Instance.Initialize() method

You can override CachedImage global defaults by calling ImageService.Instance.Initialize method in platform specific project. You can globally enable transparency channel, disable fade animation, specify own logger, timeouts and a lot more. Read more details here: link

Custom logger

Read more details here: link

Custom Transformations

Preferred way to implement custom transformations for PCL Xamarin.Forms projects is to use PCL Bait&switch technique (http://log.paulbetts.org/the-bait-and-switch-pcl-trick/).

  • Transformations should be implemented in a separate projects
    • Separate transformations project for Android - reference it in Android platform specific project

    • Separate transformations project for iOS - reference it in iOS platform specific project

    • Separate transformations project for Windows - reference it in Windows platform specific project

    • Separate transformations project for Shared Project - reference it in a Xamarin.Forms specific project (where the Page definitions are). If you don't use PCL project (but shared) you don't need it

  • All projects must have the same:
    • Assembly names - (Xamarin Studio: Project Options -> Main Settings -> Default Namespace)
    • Namespaces - (Xamarin Studio: Project Options -> Output -> Assembly Name)
    • Versions - Add eg. [assembly: AssemblyVersion("1.0.0")] to each project AssemblyInfo.cs file
  • All implemented transformations must have the same:
    • Constructors
    • Namespaces

For platform a specific guide see this: Custom Transformations Guide

Custom cache keys

You can force FFImageLoading to use custom keys for cache. For that you need to make your own ICacheKeyFactory implementation and set CachedImage.CacheKeyFactory property to it.

Custom cache key Example:

class CustomCacheKeyFactory : ICacheKeyFactory
{
	public string GetKey(ImageSource imageSource, object bindingContext)
	{
		if (imageSource == null)
			return null;

		string itemSuffix = string.Empty;
		var bindingItem = bindingContext as ListExampleItem;

		if (bindingItem != null)
			itemSuffix = bindingItem.FileName;

		// UriImageSource
		var uriImageSource = imageSource as UriImageSource;
		if (uriImageSource != null)
			return string.Format("{0}+myCustomUriSuffix+{1}", uriImageSource.Uri, itemSuffix);

		// FileImageSource
		var fileImageSource = imageSource as FileImageSource;
		if (fileImageSource != null)
			return string.Format("{0}+myCustomFileSuffix+{1}", fileImageSource.File, itemSuffix);

		// StreamImageSource
		var streamImageSource = imageSource as StreamImageSource;
		if (streamImageSource != null)
			return string.Format("{0}+myCustomStreamSuffix+{1}", streamImageSource.Stream.GetHashCode(), itemSuffix);

		throw new NotImplementedException("ImageSource type not supported");
	}
}

Custom cache key factory C# usage

var cachedImage = new CachedImage() {
	HorizontalOptions = LayoutOptions.Center,
	VerticalOptions = LayoutOptions.Center,
	WidthRequest = 300,
	HeightRequest = 300,
	CacheDuration = TimeSpan.FromDays(30),
	DownsampleToViewSize = true,
	RetryCount = 0,
	RetryDelay = 250,
	TransparencyEnabled = false,
	Source = "http://loremflickr.com/600/600/nature?filename=simple.jpg",
	CacheKeyFactory = new CustomCacheKeyFactory(),
};

Custom cache key factory XAML usage

<ffimageloading:CachedImage HorizontalOptions="Center" VerticalOptions="Center"
	WidthRequest="300" HeightRequest="300"
	DownsampleToViewSize="true"
	Source = "http://loremflickr.com/600/600/nature?filename=simple.jpg">
	<ffimageloading:CachedImage.CacheKeyFactory>
		<local:CustomCacheKeyFactory/>
	</ffimageloading:CachedImage.CacheKeyFactory>
</ffimageloading:CachedImage>