Xamarin.Forms(iOS)でSearchBarのカスタマイズで困ったこと

##はじめに
比較的おしゃれなスマホアプリを開発しようとすると、OS固有のデフォルトでは無いちょっとしたカスタマイズを行いたくなることが結構あります。
が、検索しても一発で実現方法がわからないようなケースが多々あります。今回はそんなお話。

やりたいことは比較的単純なのに、解決法が見つからなかったので情報共有してみます。

##Xamarin.Forms(iOS)のデフォルトのSearchBar

```xml
<?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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
             ios:Page.UseSafeArea="true"
             mc:Ignorable="d"
             x:Class="XamLabo2.MainPage">

    <SearchBar VerticalOptions="Start"/>
</ContentPage>
```

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/edc38e1e-3f1d-ddf0-b099-af94ec0310f5.png" width="300">|
|:-:|

こんな感じ。PlaceHolderを設定すると

```xml
    <SearchBar VerticalOptions="Start" Placeholder="Search"/>
```
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/6beb260b-c1f3-e39c-3a69-81727eeed50c.png" width="300">|
|:-:|

こんな感じ。で、何か文字を打つとxボタンとキャンセルの文字が表示される。

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/7e88fb77-36b3-2970-c79b-0d79873f17e8.png" width="300">|
|:-:|

普通ですね。
個人的にやってみたいカスタマイズは

* 虫眼鏡のアイコンを消す
* 角丸の入力欄を角丸じゃ無くす
* 灰色の背景を無くす

##虫眼鏡アイコンを無くす
SearchBarなのに虫眼鏡無くすって！って思われるかもですが、諸事情あってやってみたい時も実際あります。
英語でググってみると一番多いのは無くす（消す）よりも入力欄の右端に置きたいって要件が多いみたいです。

実際にiOSのAPIレベルで解決策を用意してくれてるわりに実現するのがなぜか困難なカスタマイズでした。
やってみたのは

```csharp
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        //
        // This method is invoked when the application has loaded and is ready to run. In this 
        // method you should instantiate the window, load the UI into it and then make the window
        // visible.
        //
        // You have 17 seconds to return from this method, or iOS will terminate your application.
        //
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            UISearchBar.Appearance.SetImageforSearchBarIcon(new UIImage(), UISearchBarIcon.Search, UIControlState.Normal);
            return base.FinishedLaunching(app, options);
        }
    }
```
SearchBarIconに空っぽのイメージを渡す方法です。これでアイコンを消すこと自体は実現できました。

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/a55b5458-fe0c-e592-231e-f383a9074d01.png" width="300">|
|:-:|

が、透明な空間が空いちゃうんですよねー透明なアイコン画像設定してるだけやから当たり前なのですが。
しかし、これも解消方法がきちんと用意されています。
SetPositionAdjustmentforSearchBarIconメソッドを使います。いかにもそれ専用って感じの長い名前ですね。

カスタムレンダラーを使います。

```csharp
[assembly: ExportRenderer(typeof(SearchBar), typeof(SearhBarCustomRenderer))]
namespace XamLabo2.iOS
{
    public class SearhBarCustomRenderer : SearchBarRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null)
            {
                var bar = this.Control;//e.NewElementでも同じ
                bar.SetPositionAdjustmentforSearchBarIcon(new UIOffset(-22, 0), UISearchBarIcon.Search);
            }
        }
    }
}
```

うまくいきました。（iPhone8+実機）

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/3f9d1f9e-5062-8a6b-b046-ac7840466f49.png" width="300">|
|:-:|

が、同じコードが現時点で最新のiOSシミュレーターだとめり込んで表示されます。
エミュレータあるあるですが、なるべく実機で検証しましょう。

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/0d797a9c-8f0a-8e16-a8d7-c130aff577b1.png" width="300">|
|:-:|

##角丸の解消と灰色の背景取り除き

APIで出来そうなところはなるべくAPIで実現するようにカスタムレンダラーを書き換えて

```csharp
[assembly: ExportRenderer(typeof(SearchBar), typeof(SearhBarCustomRenderer))]
namespace XamLabo2.iOS
{
    public class SearhBarCustomRenderer : SearchBarRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null)
            {
                var bar = this.Control;//e.NewElementでも同じ
                bar.SetPositionAdjustmentforSearchBarIcon(new UIOffset(-22, 0), UISearchBarIcon.Search);
                bar.SearchBarStyle = UISearchBarStyle.Minimal;//周囲の灰色を消す
                //SearchBarの入力欄の角丸解消
                var textField = (UITextField) bar.ValueForKey(new NSString("searchField"));
                textField.BorderStyle = UITextBorderStyle.None;
                textField.Layer.CornerRadius = 0f;
            }
        }
    }
}
```
実現できました。
カーソルが入ってないと何がなんだかわかりませんね。

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/88f9c40a-72d1-8512-9ae9-7e7020896c31.png" width="500">|
|:-:|

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/780798a8-1880-4f42-2da2-7c78dce29f3e.png" width="500">|
|:-:|

個人的にはこれでやりたかったカスタマイズは完了です。

##結果発表
もう少し調整して実際にアプリに組み込むとこういう感じ。

|カスタムレンダリング|デフォルトレンダリング|
|:-:|:-:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/38a7a769-e73e-ce1f-c5c4-0151a080bdca.png">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31737/e1a74e35-24f4-bebf-b5a3-92b8bd3da2a2.png">|

iOSの標準スタイルでも十分と言えば十分なのですが、ゴリゴリにカスタマイズしたテーマにしたい時に実現手段がわからないと厳しい。
なので、フルにカスタマイズ出来る引き出しを持っておけばよりユーザーフレンドリーなアプリを作ることもできるかと思います。

それでは、良いプログラミングライフを！