くうと徒然なるままに

モバイルアプリを作りながらバックエンドも作っています。

Xamarin.Forms で アイコンボタンを外部フォントを使って作成する

今回作るもの

アイコンがボタンになっているやつを作成します。 画像でボタンを作る場合と比べて、ベクタイメージなので、様々な解像度に対応できる利点があります。 まぁ、以下の画像を見ていただくのが速そうなので。。。

iOS

Android

UWP アプリ

外部フォントとは

iOS, Android, UWP で内蔵されているフォントの種類が違うので、今回は、外部にあるフォントを埋め込んでそれを使うことで、どのプラットフォームでもおなじフォントがつかえるようにします。
今回は、 Windows10 に標準搭載されている Segoe MDL2 Assets を使用していきます。コピーしてどこかに置いておくと扱いやすいかもしれないです。

やりかた

カスタムレンダラー

Xamarin.Forms から各プラットフォーム固有の機能をつかっていかなければいけないので、 カスタムレンダラー ってのを使っていきます。

カスタムレンダラーについては、以下のサイトが詳しいかなと

github.com

Xamarin.Forms プロジェクトでレンダラー用のクラスを作成

まずは、 Xamarin.Forms のプロジェクトで カスタムレンダラーの元となるクラスを作成していきます。
イベントとか、プロパティには特に手を加えないので、こっちは基本シンプルな感じです。
ポイントとしては、 ボタンにカスタムを加えるので、 Button クラスを継承しているところぐらいでしょうか。
自分の場合は、以下のようなクラスになりました。

CustonFontTextView.cs

using Xamarin.Forms;

namespace LTTimer.View
{
    public class CustonFontTextView : Button
    {
    }
}

Xamarin.UWP プロジェクトでレンダラー用クラスを作成

次に、 UWP アプリで実装をしていきます。

View/ 配下にコードを置きました。 また、フォントファイルを Assets/Fonts/ 配下に置きました。

ビルドアクション

  • コンテンツ

出力ディレクトリにコピー

  • コピーしない

CustonFontTextViewRenderer.cs

using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using LTTimer.UWP.View;
using Xamarin.Forms.Platform.UWP;

// おまじない
// 第一引数に、Xamarin.Forms プロジェクト内においてあるカスタムレンダラー元のクラスを指定
// 第二引数に、Xamarin.UWP プロジェクト内においてあるカスタムレンダラー先のクラスを指定
[assembly: ExportRenderer(typeof(Xamarin.Forms.Button),typeof(CustonFontTextViewRenderer))]

namespace LTTimer.UWP.View
{
    // カスタムレンダラー元が Button クラスを継承したものなので、 ButtonRenderer を継承する
    public class CustonFontTextViewRenderer: ButtonRenderer
    {
        // ネイティブビューを作成してにゃーんする
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);

            // Button のところに設定された FontFamily を取得して、どのフォントを使うのか選択するのに使う
            // もし、設定してなかったら NullReferenceException がでちゃうから、 Null チェックしつつ取得してく
            var fontfamily = e.NewElement?.FontFamily;
            
            // 取得できてなかった時のために
            if (fontfamily != null)
            {
                var element = (Button) Control;
                // UWP は比較的簡単にかける
                element.FontFamily = new FontFamily(e.NewElement.FontFamily);
            }
        }
    }
}

Xamarin.Android プロジェクト で レンダラー用のクラスを作成

コードは View フォルダに配置しました。 Android はフォントファイルを Resource/Fonts に置く

ビルドアクション

  • AndroidAsset

出力ディレクトリにコピー

  • コピーしない

CustonFontTextViewRenderer.cs

using Android.Graphics;
using Android.Widget;
using LTTimer.Droid.View;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Button = Xamarin.Forms.Button;

// おまじない
// 第一引数に、Xamarin.Forms プロジェクト内においてあるカスタムレンダラー元のクラスを指定
// 第二引数に、Xamarin.UWP プロジェクト内においてあるカスタムレンダラー先のクラスを指定
[assembly:ExportRenderer(typeof(Button),typeof(CustonFontTextViewRenderer))]

namespace LTTimer.Droid.View
{
    // カスタムレンダラー元が Button クラスを継承したものなので、 ButtonRenderer を継承する
    public class CustonFontTextViewRenderer: ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);

            // Button のところに設定された FontFamily を取得して、どのフォントを使うのか選択するのに使う
            // もし、設定してなかったら NullReferenceException がでちゃうから、 Null チェックしつつ取得してく
            var fontFamily = e.NewElement?.FontFamily?.ToLower();
            
            if (fontFamily != null)
            {
                var label = (TextView)Control;
                // Android でLabelのフォントファミリーを設定するには、 Typeface.CreateFromAsset メソッドを使う
                // フォントサイズは特に変えたくなかったので、第二引数にもともとのフォントサイズを代入しています。
                label.Typeface = Typeface.CreateFromAsset(Context.Assets,e.NewElement.FontFamily);
            }
        }
    }
}

## Xamarin.iOS プロジェクト で レンダラー用のクラスを作成

ios は、 フォントファイルの埋め込みが少し面倒でしたにゃーんした
今回も、 View 配下にコードを配置しました。

CustonFontTextViewRenderer.cs

using LTTimer.iOS.View;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

// おまじない
// 第一引数に、Xamarin.Forms プロジェクト内においてあるカスタムレンダラー元のクラスを指定
// 第二引数に、Xamarin.UWP プロジェクト内においてあるカスタムレンダラー先のクラスを指定
[assembly:ExportRenderer(typeof(Button),typeof(CustonFontTextViewRenderer))]

namespace LTTimer.iOS.View
{
    // カスタムレンダラー元が Button クラスを継承したものなので、 ButtonRenderer を継承する
    public class CustonFontTextViewRenderer: ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            // Button のところに設定された FontFamily を取得して、どのフォントを使うのか選択するのに使う
            // もし、設定してなかったら NullReferenceException がでちゃうから、 Null チェックしつつ取得してく
            var fontFamily = e.NewElement?.FontFamily;
            if (fontFamily != null)
            {
                var element = Control;
                // UIFont のスタティックメソッドである、 **FromName()** を使い作成したインスタンスを 設定していく
                element.TitleLabel.Font = UIFont.FromName(fontFamily,element.TitleLabel.Font.PointSize);
            }
        }
    }
}

iOS のフォントの設定はいろいろ大変

iOS のフォントの設定等はいろいろ大変だったので別セクションにします。

いつもどうり、Xamarin.iOS のプロジェクトにフォントを追加

iOS でも、ほかのプラットフォームと同じように、 Resource/Fonts/ 配下に配置します。

ビルドアクション

  • AndroidAsset

出力ディレクトリにコピー

  • コピーしない

info.pilot へ設定の追加

Xamarin.iOS のプロジェクトにある、 info.pilot ファイルを開きます。

画面の左下にある以下のようなボタンをクリックします。

テキストボックス的なのがでてくるので、 Font Provided by application と入力します。途中で入力補完がかかるので、 それを選択します。

次に、 String と書かれた部分の右側をダブルクリックして、文字を編集できるようにします。

そこへ、 Fonts/{FontFileName} と入力します。

完成したので アプリに表示させてみる

MainPage.Xaml を開き、追記して表示していきます。 ポイントは、 OnPlatform を使い、iOS,Android,UWP で処理を切り分けているところでしょうか。

Font-Family で設定する値

iOS

フォント名、 MacFontBook でフォントを開いたときに、 PostScript名 というところの名前を入力します。

f:id:kuxumarin:20170317152223p:plain

Android

Resource フォルダを起点として、フォントファイルまでのパスを記入します。

UWP アプリ

今回使ったのは、 Windows 10 なら標準に入っているということで、 フォント名だけを入れるだけでおkです。

MainPage.xaml

<view:CustonFontTextView Text="&#xE713;">
            <view:CustonFontTextView.FontFamily>
                <OnPlatform x:TypeArguments="x:String"
                    iOS="Segoe MDL2 Assets"
                    Android="Fonts/SEGMDL2.TTF"
                    WinPhone="Segoe MDL2 Assets">
                </OnPlatform>
            </view:CustonFontTextView.FontFamily>
        </view:CustonFontTextView>

動かしてみた

iOS

Android

UWP アプリ

つらかったところ

フォントを設定しても表示されない

iOSInfo.Pilot へ設定を追加しないといけないところに気づけなくて、ハマった

パスをチェック

タイポしてると フォントファイルを取得できずに NullReferenceException が発生してアプリが落ちることがたまによくあった

再起動すればOK

最後に悩んだら、再起動すればなんとかなった。

参考記事

blog.yamamoworks.net

www.sirochro.com

https://docs.microsoft.com/ja-jp/windows/uwp/style/segoe-ui-symbol-font#a-namelayering-and-mirroringa重ね合わせとミラーリングdocs.microsoft.com