Xamarin.Formsで画像にTintColorを適用する - カスタムレンダラー編


アイコン画像の色、変えたいことありますよね。
色ごとにアイコン画像を作るのは面倒なので、TintColor設定でいつでもどこでも変えられるようにしてみます。

![ic_local_florist_black_24dp_2x.png](https://qiita-image-store.s3.amazonaws.com/0/169798/8f2ecc58-e671-d476-03d1-ef1319afc481.png "ic_local_florist_black_24dp_2x.png")


この花の形をした画像の色を変えてみたいと思います。

カスタムレンダラーを使いたくないという方は[こちら](http://qiita.com/sudachi808/items/b00d64d4746136bb122b)をどうぞ。

## 実装

### PCL

Image コントロールを継承したカスタムコントロールを作成します。
TintColorをXAMLから設定できるようにバインド可能プロパティを追加しておきます。

```csharp:TintImage.cs
using Xamarin.Forms;

namespace MyApp
{
    public class TintImage : Image
    {
        public static readonly BindableProperty TintColorProperty = BindableProperty.Create(
            "TintColor", typeof(Color), typeof(TintImage), Color.Default
        );
        
        public Color TintColor {
            get { return (Color)GetValue(TintColorProperty); }
            set { SetValue(TintColorProperty, value); }
        }
    }
}
```

```xml:XAML
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MyApp"
    x:Class="MyApp.MyAppPage">
    
    <StackLayout VerticalOptions="Center" HorizontalOptions="Center">
    
        <local:TintImage Source="sample.png" />
        <local:TintImage Source="sample.png" TintColor="Red"/>
        <local:TintImage Source="sample.png" TintColor="Blue" />
    
    </StackLayout>
    
</ContentPage>
```

あとは各プラットフォームごとにカスタムレンダラーを作成します。

### iOS

```csharp:TintImageRenderer.cs
using System;
using System.ComponentModel;

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using UIKit;

using MyApp;
using MyApp.iOS;

[assembly: ExportRenderer(typeof(TintImage), typeof(TintImageRenderer))]

namespace MyApp.iOS
{
    public class TintImageRenderer : ImageRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);
            
            if (e.NewElement != null) {
                SetTint();
            }
        }
        
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            
            if (e.PropertyName == nameof(TintImage.TintColor)) {
                SetTint();
            }
        }
        
        protected void SetTint()
        {
            var element = (TintImage)Element;
            
            if (element.TintColor == Color.Default) {
                if (Control.Image != null) {
                    Control.Image = Control.Image.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
                }
                Control.TintColor = null;
            } else {
                if (Control.Image != null) {
                    Control.Image = Control.Image.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
                }
                Control.TintColor = element.TintColor.ToUIColor();
            }
        }
    }
}
```

### Android

```csharp:TintImageRenderer.cs
using System;
using System.ComponentModel;

using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

using Android.Graphics;

using MyApp;
using MyApp.Droid;

[assembly: ExportRenderer(typeof(TintImage), typeof(TintImageRenderer))]

namespace MyApp.Droid
{
    public class TintImageRenderer : ImageRenderer
    {
        /**
         * Xamarin.Forms 2.5 以降では引数なしのコンストラクタが obsolete になっているので、
         * 引数に Context をとるコンストラクタを定義します。
         */
        public TintImageRenderer(Android.Content.Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);
            
            if (e.NewElement != null) {
                SetTint();
            }
        }
        
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            
            if (e.PropertyName == nameof(TintImage.TintColor)) {
                SetTint();
            }
        }
        
        protected void SetTint()
        {
            var element = (TintImage)Element;
            
            if (element.TintColor == Xamarin.Forms.Color.Default) {
                Control.ClearColorFilter();
            } else {
                Control.SetColorFilter(element.TintColor.ToAndroid(), PorterDuff.Mode.SrcIn);
            }
        }
    }
}
```

## 結果

各プラットフォームの Resources に画像ファイルをコピーして実行すると...

### iOS

![result_ios.png](https://qiita-image-store.s3.amazonaws.com/0/169798/ea0de981-f8a6-80ff-bf13-4d5e6b582bef.png "result_ios.png")

### Android

![result_android.png](https://qiita-image-store.s3.amazonaws.com/0/169798/5f712142-8241-89ba-9703-9f219bcefe71.png "result_android.png")


黒の単色だった画像に色がつきました。

とりあえずは以上です。

## こちらを
参考にし
パクリました

https://forums.xamarin.com/discussion/56568/how-to-set-image-tint-in-windows-rt