Xamarin.FormsでNative View（Binding Objective-C Libraries使用）の導入メモ

<font color="red">2017/04/28 AndroidのNativeViewの利用について説明追加</font>

Xamarin.Forms2.3.3から追加されたNative Viewsを使用したメモ
この記事はNative Viewsの導入方法を説明する内容ではなく、
導入に際してはまった問題や、ここはまるなーと思ったポイントのメモとなる

導入については下記公式の記事を見るかぴーさんのブログを見るといい

 - [Native Views (Adding platform-specific controls to XAML files)]
(https://developer.xamarin.com/guides/xamarin-forms/user-interface/native-views/)

バインディングライブラリについては下記を参照

 - [Binding Objective-C Libraries]
(https://developer.xamarin.com/guides/cross-platform/macios/binding/objective-c-libraries/)

## 前提条件
- 母艦はWin10でVisualStudio2015使用（MacBookProも別途利用）
- Xamarin.FormsアプリPCLプロジェクトでiOSとAndroidに対応
- Xamarin.Formsのバージョンは2.3.3

## 目的
Xamarin.Forms PCLプロジェクトでiOSのNative Views(外部SDK提供物)を利用する

## 導入理由
サードパーティのObjective-Cライブラリを使用する必要があり、
ライブラリで提供されるネイティブViewの利用が必須だった為

## はまりポイント
### VS2015ではiOSバインディングプロジェクトのインテリセンスが効かない
NativeViewsは関係なくバインディングプロジェクトの問題
インテリセンスが効かないのはXamarinの<strike>バグ</strike>仕様である
対処法はプロジェクト参照をやめて作成されたdllをアセンブリ参照すれば良い
詳細な情報が知りたい場合は下記のURLを参照
https://bugzilla.xamarin.com/show_bug.cgi?id=18815
https://forums.xamarin.com/discussion/16164/intellisense-on-projects-referencing-a-binding-project-doesnt-work

### PCLの場合NativeViewsはXamlのみ対応（コードビハインドは非対応）
コードビハインドで作成していた場合、Xamlで書き直す必要がある
### XamlCompilationOptions.Compileでは動作しない
ビルドエラーも実行時エラーも発生しないで、単純に動かないだけなのでわかりにくい 
テンプレートからXamlを作成している場合、初期でCompile設定になっている為、気が付かない恐れがある
### アセンブリ名を間違えた場合の例外がわかりにくい
例えばアセンブリ名が「MyApp.iOS」が正解なところ間違えて「MyAppiOS」と入力して実行した場合に発生する例外メッセージは
「MyView not found in xmlns clr-namespace:MyApp.iOS;assembly=MyAppiOS;targetPlatform=iOS
」となり、
アセンブリ名を間違えているのに、あたかもMyAppiOSというアセンブリは存在するが使いたいViewが見つからない印象を与える

```lang:xaml
xmlns:ios="clr-namespace:MyApp.iOS;assembly=MyAppiOS;targetPlatform=iOS"
<ios:MyView />
```

### プロパティに対するBindingに対応しているが、Getterを作っておかないと実行時例外が発生する  
動作する例

```lang:Xaml
<ios:MyView Text="test" />
```

```lang:C#
public class MyView : UIView
{
    public string Text { set; }
}
```

上記のXamlコードを下記のようにBindingに書き直すと動かなくなる
実行時例外が発生するがメッセージは上記アセンブリ名を間違えたときと同じメッセージしか得られない

```lang:Xaml
<ios:MyView Text="{Binding TestText}" />
```

動作させるにはC#側にgetterを作成する

```lang:C#
public class MyView : UIView
{
    public string Text { get; set; }
}
```

Bindingを利用しないで直接代入する場合はGetterがなくても動作するのが厄介

### 使いたいNativeViewsの初期化に指定されたイニシャライザの利用が必要だった
ライブラリで提供されているViewが既定のコンストラクタを使って初期化すると動かない仕様であった為、単純にXaml側に記載しても動かなかった
具体例を挙げて説明すると
ライブラリで提供されているNativeViewのクラス名が「SdkButton」だった場合

```lang:Xaml
<ios:SdkButton />
```

と単純に書いても動作しないViewであった
このSdkButtonクラスには静的なメソッドでSdkButtonインスタンスを返すメソッド「ButtonWithType(UIButtonType type)」が用意されており
このメソッドを通してインスタンス化しないと使えないつくりとなっていた

なので下記のようなつくりにする必要があった

```lang:Xaml
<ios:MySdkButton />
```

```lang:C#
public class MySdkButton : UIView
{
    private SdkButton _SdkButton;

    public MySdkButton()
    {
        _SdkButton = MySdkButton.ButtonWithType(UIButtonType.RoundedRect);
        AddSubview(_SdkButton);
    }
}
```

SdkButtonを直接Xamlで使うのではなく、MySdkButtonというクラスを通して
SdkButtonを表示するようにする

MySdkButtonは内部にSdkButtonをもっておりここで初期化を行う

## Viewの初期化に引数が必要(Android)
Androidの場合、Contextを引数に取るものが多い
XamlからContextを渡すには下記のようにする

```lang:xaml
xmlns:formsAndroid="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android"

<droid:MySdkButton x:Arguments="{x:Static formsAndroid:Forms.Context}" />
```

```lang:C#
public class MySdkButton : SdkButton
{
    public (Context context) : base(context) { }
}
```