Xamarin.iOSでiOS向けカスタムキーボードを作る

# はじめに

Visual Studio 2017 community(2017/9/22時点)のXamarin.iOSを使ってiOS向けのカスタムキーボードを作る方法およびコツをまとめます。

# 1.新規ソリューションを立ち上げる

<img width="500" alt="説明1.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/cb25f18c-743d-c4cb-0eb1-13cfad82dae7.png">
まずiOSの単一ビューアプリを選択します。

<img width="500" alt="説明2.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/c2a5f62c-af1d-316c-6a40-d2fea3919658.png">
続いて、アプリ名を設定します。デバイス設定はここではひとまずiPhoneのみにチェックをします。

<img width="500" alt="説明3.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/66ce6eee-d848-7c98-816b-078883aa91ed.png">

<img width="500" alt="説明4.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/d2cd0ad1-4766-1303-2bc7-8d8378461b4a.png">
そのまま次へを押してソリューションを立ち上げます。

# 2.キーボードのExtensionプロジェクトを追加する

<img width="500" alt="説明5.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/619cf4ce-390b-b762-737d-7371a581b06a.png">
ソリューションのルートから「新しいプロジェクトを追加」を選択します。

<img width="500" alt="説明6.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/bc1b9dfe-978a-3199-2661-179a302db308.png">
iOSのExtension->Custom Keyboard Extentionを選択します。

<img width="500" alt="説明7.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/6e2a659f-273a-1951-5125-553e9a9229ff.png">
拡張機能名がキーボードの名前になります。

<img width="500" alt="説明8.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/5739f135-c960-aa9f-7052-f5d63119aeb5.png">

<img width="262" alt="説明9.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/4b2a38d9-d096-0171-f6d0-1e26f21979fb.png">

そのまま次へを選択すると、Custom Keyboardのプロジェクトが追加されます。

# 3.キーボードのViewとそれを司るクラスファイルを作成する

<img width="500" alt="説明10.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/c68403ab-0c86-eecb-f977-f1e83e114048.png">
元からあったiOSCustomKeyboardを副クリックして「新しいファイルを追加」を選択します。

<img width="500" alt="説明11.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/857bf44b-1654-e1f4-7a9e-5a3e36503b68.png">
iOS->Table View Cellを選択して、こちらも名前をKeyboardViewにします。これはキーボードのイベントなどを扱うために用意するクラスです。本当はTabel View CellではなくただのViewが良いのですが、追加方法が見つからなかったため、Table View Cellを改造する形で進めます。

<img width="263" alt="説明12.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/2f5d5aeb-c67d-339a-6ca4-fd9fabbe8bf6.png">
そうすると
KeyboardView.csとKeyboardView.designer.cs、KeyboardView.xibが追加されます。

ここで、KeyboardView.csを選択してください。
KeyboardView.csの最低限の編集を行います。
もともとのソースコードは、

```c#:~KeyboardView.cs
using System;

using Foundation;
using UIKit;

namespace iOSCustomKeyboard
{
    public partial class KeyboardView : UITableViewCell
    {
        public static readonly NSString Key = new NSString("KeyboardView");
        public static readonly UINib Nib;

        static KeyboardView()
        {
            Nib = UINib.FromName("KeyboardView", NSBundle.MainBundle);
        }

        protected KeyboardView(IntPtr handle) : base(handle)
        {
            // Note: this .ctor should not contain any initialization logic.
        }
    }
}
```

上のようになっていると思います。これを下のように書き換えてください。

```c#:~KeyboardView.cs
using System;

using Foundation;
using UIKit;

namespace TestKeyboard
{
    public partial class KeyboardView : UIView
    {
        static KeyboardView()
        {
            
        }

        protected KeyboardView(IntPtr handle) : base(handle)
        {
            
        }
    }
}
```

namespaceと親クラスを変更するのを忘れないでください。

<img width="300" alt="説明13.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/4f2b570b-1631-1683-f76b-dad176b5ed5d.png">
そうしましたら、UITableViewに特化したxibになってしまっていて使えないため、KeyboardView.xibを完全に削除してください。

<img width="719" alt="説明14.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/34a22a6d-a5af-0c60-c57d-6514b1dc4bb3.png">
先ほどと同様にiOSCustomKeyboardを副クリックして「新しいファイルを追加」を選択し、今度はiOS->Viewを選択して、名前をKeyboardViewにして作成します。これでキーボードのレイアウトを編集するxibファイルを生成しなおします。

<img width="263" alt="説明13.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/14f16db1-f52d-b789-095b-ec4beff79ff4.png">
三つのファイルがiOSCustomKeyboardディレクトリ内に追加されているのを確認してください.

<img width="262" alt="説明15.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/12cd5134-9609-b494-6212-b7c8c95e65fe.png">
これをTestKeyboardディレクトリにドラッグ&ドロップで移します。

このタイミングでKeyboardView.designer.csを選択してnamespaceをTestKeyboardに変更してください。

```c#:KeyboardView.designer.cs
using Foundation;

namespace TestKeyboard
{
    [Register("KeyboardView")]
    partial class KeyboardView
    {
        void ReleaseDesignerOutlets()
        {
        }
    }
}
```

そうしたらKeyboardView.xibを開いてください。

<img width="1000" alt="説明16.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/43371089-1247-bc8a-6d1c-9bc3ec26f4fe.png">
Viewを選択してプロパティのWidgetのClassをKeyboardViewにしてください。先ほどのnamespaceの変更によってここにClassの候補が表示されるようになります。

これで下準備は完了です。

# 4.キーボードのViewをレイアウトする

引き続きKeyboardView.xibを編集します。

<img width="400" alt="説明17.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/51ae3534-af8c-3a17-97e7-07acf737440b.png">

左上のVIEW ASがGenericとなっているので、iPhone6など自分のデザインしやすいものにしてください。

<img width="321" alt="説明18.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/7c6993e2-d253-eb2c-3dec-e2f80d9a72c2.png">
Viewを選択してプロパティのWidgetのSimulated Metricsを編集してSizeをFreeformに、Status BarをNoneに変更してください。

<img width="1000" alt="説明19.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/f085d83f-79ca-0a28-de56-4a12aee54a3e.png">
ツールボックスからButtonを選んでドラッグ&ドロップしてButtonを4つ配置してください。

<img width="816" alt="説明20.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/b9ddca42-a96b-16cb-5f46-2c833b028052.png">
Buttonの大きさをいい感じにして、表示している文字をA, B, Next, Spaceにしてください。同時にIdentityのNameにAButton、BButton、NextButton、SpaceButtonと名前を設定してください。

<img width="826" alt="説明21.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/1791b7ee-fbee-c124-5132-0b9fe9b0cc70.png">
Aボタンを選択して、プロパティのEventsを開き、TouchのUp Insideの欄にPushKeyと入力してReturnキーを押してください。この時、イベントハンドラの名前を大文字から始まるようにしないとエラーになります。

<img width="1000" alt="説明22.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/06fe2b9d-ffe9-2291-07ab-45c9b1f45eb1.png">
自動的にKeyboardView.csが開き、イベントハンドラの追加が行えます。追加位置はアローキーにて変更できます。

<img width="1000" alt="説明23.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/4b1c041b-d0f5-3c07-e524-b327e5f1ed6d.png">
Returnキーを押して確定するとこのようにボタンを押した時のイベントハンドラのコードが自動で挿入されます。

今度はBボタンを選択し、Aボタンと同じイベントハンドラに接続します。
<img width="793" alt="説明24.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/78b7f208-52f8-2acc-9c5d-c241c7b8add3.png">
すでに一度作ってあるイベントハンドラは候補から選択できます。

同様にしてNextボタンはPushNext、SpaceボタンはPushSpaceというイベントハンドラ名でコードと関連付けを行ってください。

結果KeyboardView.csはこのようになります。

```c#:KeyboardView.cs
using System;

using Foundation;
using UIKit;

namespace TestKeyboard
{
    public partial class KeyboardView : UIView
    {
        static KeyboardView()
        {
            
        }

        protected KeyboardView(IntPtr handle) : base(handle)
        {
            
        }

        partial void PushKey(UIButton sender)
        {
            throw new NotImplementedException();
        }

        partial void PushNext(UIButton sender)
        {
            throw new NotImplementedException();
        }

        partial void PushSpace(UIButton sender)
        {
            throw new NotImplementedException();
        }
    }
}

```

ちなみにKeyboardView.designer.csの方は自動で編集が進み、このようになっています。

```c#:KeyboardView.designer.cs
using Foundation;
using System;
using System.CodeDom.Compiler;

namespace TestKeyboard
{
    [Register ("KeyboardView")]
    partial class KeyboardView
    {
        [Outlet]
        [GeneratedCode ("iOS Designer", "1.0")]
        UIKit.UIButton AButton { get; set; }

        [Outlet]
        [GeneratedCode ("iOS Designer", "1.0")]
        UIKit.UIButton BButton { get; set; }

        [Outlet]
        [GeneratedCode ("iOS Designer", "1.0")]
        UIKit.UIButton NextButton { get; set; }

        [Outlet]
        [GeneratedCode ("iOS Designer", "1.0")]
        UIKit.UIButton SpaceButton { get; set; }

        [Action ("PushKey:")]
        [GeneratedCode ("iOS Designer", "1.0")]
        partial void PushKey (UIKit.UIButton sender);

        [Action ("PushNext:")]
        [GeneratedCode ("iOS Designer", "1.0")]
        partial void PushNext (UIKit.UIButton sender);

        [Action ("PushSpace:")]
        [GeneratedCode ("iOS Designer", "1.0")]
        partial void PushSpace (UIKit.UIButton sender);

        void ReleaseDesignerOutlets ()
        {
            if (AButton != null) {
                AButton.Dispose ();
                AButton = null;
            }

            if (BButton != null) {
                BButton.Dispose ();
                BButton = null;
            }

            if (NextButton != null) {
                NextButton.Dispose ();
                NextButton = null;
            }

            if (SpaceButton != null) {
                SpaceButton.Dispose ();
                SpaceButton = null;
            }
        }
    }
}
```

最後にボタンがわかるように背景色を設定しましょう。
Viewやボタンを選択してプロパティのWidgetのViewのBackground項目を設定すると背景色を変更できます。
<img width="809" alt="説明25.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/f93b4afe-af5c-210b-0554-c10080196495.png">

レイアウト周りの下準備は以上です。

# 5.コーディング

KeyboardView.csを開きます。まずは下のように追記してください。

```c#:KeyboardView.cs
using System;

using Foundation;
using UIKit;

namespace TestKeyboard
{
    public interface KeyboardViewDelegate
    {
        void selectKey(string key);
        void selectNext();
        void selectSpace();
    }

    public partial class KeyboardView : UIView
    {
        public KeyboardViewDelegate delegate_;

        static KeyboardView()
        {}

        protected KeyboardView(IntPtr handle) : base(handle)
        {}

		public override void AwakeFromNib()
		{
            AButton.Layer.CornerRadius = 10;
            BButton.Layer.CornerRadius = 10;
            NextButton.Layer.CornerRadius = 10;
            SpaceButton.Layer.CornerRadius = 10;
		}

        partial void PushKey(UIButton sender)
        {
            delegate_.selectKey(sender.CurrentTitle);
        }

        partial void PushNext(UIButton sender)
        {
            delegate_.selectNext();
        }

        partial void PushSpace(UIButton sender)
        {
            delegate_.selectSpace();
        }
    }
}
```

続いて、KeyboardViewController.csを編集します。
まずは余計なものを消して綺麗にしましょう。

```c#:KeyboardViewController.cs
using System;

using ObjCRuntime;
using Foundation;
using UIKit;

namespace TestKeyboard
{
    public partial class KeyboardViewController : UIInputViewController
    {

        protected KeyboardViewController(IntPtr handle) : base(handle)
        {}

        public override void DidReceiveMemoryWarning()
        {
            base.DidReceiveMemoryWarning();
        }

        public override void UpdateViewConstraints()
        {
            base.UpdateViewConstraints();
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
        }

        public override void TextWillChange(IUITextInput textInput)
        {}

        public override void TextDidChange(IUITextInput textInput)
        {}
    }
}
```

綺麗にできたら下のように追記してください。

```c#:KeyboardViewController.cs
using System;

using ObjCRuntime;
using Foundation;
using UIKit;

namespace TestKeyboard
{
    public partial class KeyboardViewController : UIInputViewController, KeyboardViewDelegate
    {
        KeyboardView kv;

        protected KeyboardViewController(IntPtr handle) : base(handle)
        {}

        public override void DidReceiveMemoryWarning()
        {
            base.DidReceiveMemoryWarning();
        }

        public override void UpdateViewConstraints()
        {
            base.UpdateViewConstraints();
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            kv = UINib.FromName("KeyboardView", null).Instantiate(this, null)[0] as KeyboardView;
			kv.delegate_ = this;
			View.AddSubview(kv);
        }

        public override void TextWillChange(IUITextInput textInput)
        {}

        public override void TextDidChange(IUITextInput textInput)
        {}

        public void selectKey(string key)
        {
            TextDocumentProxy.InsertText(key);
        }

        public void selectNext()
        {
            AdvanceToNextInputMode();
        }

        public void selectSpace()
        {
            TextDocumentProxy.InsertText(" ");
        }
    }
}
```

コーディングは以上です。

# 6.実行

レイアウトをしたサイズのiPhoneをシミュレータで指定して実行しましょう。真っ白な画面が出てくると思います。
ホームボタンを押すか、shift + command + H でホーム画面を表示して、環境設定を開き、一般->言語->キーボードから自作したiOSCustomKeyboardを追加しましょう。
リマインダアプリなどでキーボードの動作を確認できます。

<img width="200" alt="説明26.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/4a73b506-0558-a050-a393-6d6fddc2e511.png"><img width="200" alt="説明27.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/3d3a9b27-369d-d72f-0112-6ae5196634ee.png"><img width="200" alt="説明28.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/83eb2f92-32ab-b6cc-2b3d-5a6a2c0464db.png">


# 7.終わりに

XamarinでのiOSカスタムキーボード開発の基礎の基礎ですが、なんとかやり方を確立できてよかったです。