humangas's blog

自分用のメモなので雑です。

xcodeでiosアプリのカスタムキーボードを作成する方法(UITextfieldのinputViewを独自UIに置き換える)

ググれば色々出たけど、欲しい情報が散らばっていたので自分用に纏めました。

やりたいことは、別ファイルで今後使い回したいUI部品を作成しておいて、それをコントローラから呼び出す形で使いたい。です。しかも、今回はカスタムキーボードを使いたいので、その部品をデフォルトで表示されるキーボードと置換えます。

Objective-C的に言えば、InterfaceBuilderで作成したUI部品(xibファイル)に対応する実装クラスをViewControllerの初期化時(viewDidLoad)にNewして、それをUITextFieldのキーボードを保持するinputViewプロパティに置き換える。という感じです。

完成形

こんな感じになります。
ちなみに、このキーボードはボタンのイメージ画像は作成しておらず、カラーとテキストのみで作成しています。

f:id:Humangas:20140519184117p:plain

なぜ、やりたい?

デフォルトの数値キーボードもあるんですが、キーボードを閉じるボタン(return)がありません。
はじめは、閉じるボタンを付けるために色々調べていてそれはそれで出来たのですが、その内全部オリジナルのキーボードが表示したくなったのでやることにしました。

そうすると、今度は全部コードでやるのはUIのレイアウトとか辛いのでInterfaceBuilderでUI部品作って、それを気軽に使えないか? ・・・と調べるうちにココまで来た感じです。

ちなみに、デフォルトの数値キーボードはこんな感じですね。 コード上でkeyboardType = UIKeyboardTypeNumberPadとすれば、これが表示されます。

f:id:Humangas:20140519184222p:plain

試した環境

  • Xcode:5.1.1
  • iOS SDK:7.1

ポイント

以下は、自分が調べた・ハマったところです。
このポイントを抑えれば、後はSingle View Application とかで普通にアプリを作る時のやり方でいけました。

  • カスタムキーボード.xibファイルとそれに対応するカスタムキーボード実装クラスを作成
    • → で、カスタムキーボード実装クラスの初期化処理で作成したxibファイル(UINib)をロードし、selfに置き換えればそのViewを使える
    • → で、メイン側(ViewControllerとか)で、そのカスタムキーボードインスタンスを作成すれば使える
  • カスタムキーボード.xibファイルのAutosizingを横調整のみに設定する
    • →そうしないと、デフォルトキーボードサイズ(縦が)になってしまう
  • UITextFieldのinputViewプロパティをカスタムキーボードクラスに置き換える
    • → コレで、キーボードを置き換えられる。

やり方

前述のポイントのところだけ書いてます。

カスタムキーボード作成

New File> iOS> User Interface> View でxibファイルを作成します。

f:id:Humangas:20140519193401p:plain

New File> iOS> Cocoa Touch> Objective-C class で、先に作成したxibファイルに対応する実装クラスを作成します。
ただ、この時点ではxibファイルと実装クラスはまだ紐ついていません。その設定は次に示します。

f:id:Humangas:20140519195917p:plain

作成するクラスは、UIViewを継承したクラスにします。

f:id:Humangas:20140519195854p:plain

作成したxibファイルを適当にいじってお好きなキーボードを作成します。
ここでのポイントは特にありませんが、この記事の始め(最終形)に示した私が作成しているキーボードの場合は、カラーを最近の流行りのフラットデザインカラーにしています。

このサイト:http://paletta.mrk1869.com/でカラーを選択して、16進数をこのサイト:http://www.peko-step.com/tool/tfcolor.htmlでRGBに変換して、InterfaceBuilderの各カラー設定に反映しました。

ちなみに、ボタンの大きさは、width:63px、height:63pxで、ボタンとボタンの間は1px空けています。 また、それぞれのカラーは以下の通りです。

  • キーボード背景:R17,G15,B23(#110f17)
  • ボタンテキスト:R239,G238,B244(#efeef4)
  • 数値ボタン背景:R60,G57,B70(#3c3946)
  • 数値ボタンフォントサイズ:20(「return」と「◁☓」のみ15)
  • 数値以外のボタン背景:R39,G38,B40(#272628)

カスタムキーボード.xibと実装クラスの紐付け設定(InterfaceBuilder)

Custom Class を作成した実装クラス名にします。

f:id:Humangas:20140519195445p:plain

いらないBar は、Noneにしておきます。

f:id:Humangas:20140519195946p:plain

Autosizing を横のみ自動設定にします。
これをしておかないと、inputViewを置換えてもデフォルトのキーボードサイズの縦サイズになってしまいます。

f:id:Humangas:20140519195443p:plain

カスタムキーボード.xibと実装クラスの紐付け設定(ソースコード)

CustomNumKeyboard.m(前述で作成したカスタムキーボード実装クラス)のinitでxibファイルをロードして自身(UIView)にセットします。
これで、xibファイルとそれに対応する実装クラスが関連づけられました。

-(id)init{
    
    self = [super init];
    if (self) {
        // UINibで作成したxibファイルを取得
        UINib *nib = [UINib nibWithNibName:@"CustomNumKeyboard" bundle:nil];
        // 0番目の要素(一番最初に置いたUIView)を取得して自分自身にセット
        // ※ self は、initメソッド内であれば代入できる
        self = [nib instantiateWithOwner:nil options:nil][0];
    }
    return self;
}

カスタムキーボードのボタン押下時アクション(メソッド)

対応付けただけでは、当然動かないので、各ボタンのアクションをCustomNumKeyboradクラスに実装します。

各アクション(メソッド)はIBActionで実装し、CustomNumKeyborad.xibファイルの各ボタンとInterfaceBuilder上で紐付けます。
数値キーボード押下時の処理とキーボードを閉じる処理だけ抜粋しておきます。

この実装では、各キーボードボタンのタグに数値ボタンに対応する数値を設定しておき、そのままテキストフィールドに表示するようにしています。

/**
 * 数値ボタン押下時処理
 *
 * @param sender 押下されたUIButton
 */
-(IBAction)numButton:(UIButton *)sender{
    NSString *numtag = [NSString stringWithFormat:@"%d", sender.tag];
    self.activeTextField.text = [self.activeTextField.text stringByAppendingString:numtag];
}

/**
 * return ボタン押下時処理
 * このViewに設定されたTextFieldのキーボードを閉じる
 */
-(IBAction)closeKeyboard:(id)sender{
    [self.activeTextField resignFirstResponder];
}

ここで、self.activeTextFieldというプロパティにアクションしていますが、これが処理したいUITextFieldです。
このUITextFieldは、CustomNumKeyboardのインスタンス生成時にプロパティに設定しておきます。設定できるように以下のようにヘッダクラスにプロパティを宣言しておきます。

@property (nonatomic, retain)UITextField *activeTextField;

コントローラからカスタムキーボードを利用

作成したカスタムキーボードは、以下のようにコントローラからインスタンス生成し、テキストフィールドのinputViewに設定すれば利用できます。InterfaceBuilder上でMain.stroyboardにテキストフィールドを接続しておいて下さい。

@implementation ViewController
{
    IBOutlet UITextField *kingakuTx;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    CustomNumKeyboard *cnkb = [CustomNumKeyboard new];
    cnkb.activeTextField = kingakuTx;
    kingakuTx.inputView = cnkb;
}

だぁーと書いたけど、説明がわかりづらいので気が向いたらちゃんと書きなおそう。いつか。。