はじめに
UITextField や UITextView で入力する際はキーボードの入力タイプが色々用意されていますが実はこのキーボード色々カスタムができます!
たぶん実用的なのは UIPickerView とか UIDatePicker を表示したりとかです(なんか昔半角カナ用キーボードを作った記憶があるけどあんまよくなかった
)。
これからはもう UIKit を使うことも少なくなっていくのかもしれませんが備忘録として。。。
*注:「つくろう」って書きましたがとくにカスタムキーボードを薦めるものではありません。
UITextFieldをカスタマイズ
試しに UITextField のキーボードをカスタムしてみます。やり方は簡単で inputView に任意の View を設定するだけで OK です![]()
// こんなのとか
let keyboard = CustomKeyboardView(frame: .init(origin: .zero, size: .init(width: 284, height: 284)))
keyboard.delegate = self
textField.inputView = keyboard
// こんなのとか
let picker = UIPickerView()
picker.delegate = self
picker.dataSource = self
textField.inputView = picker
あとはデリゲートとかで入力文字を受け取って UITextField に表示するだけです。
こんな感じ。
| カスタム View | ピッカー |
|---|---|
![]() |
![]() |
カスタム View はこんな感じです。
xib
protocol CustomKeyboardViewDelegate: AnyObject {
func customKeyboardView(_ customKeyboardView: CustomKeyboardView, didSelectKey key: String)
}
final class CustomKeyboardView: UIView {
weak var delegate: CustomKeyboardViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
loadNib()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
loadNib()
}
private func loadNib() {
let view = Bundle.main.loadNibNamed("CustomKeyboardView", owner: self, options: nil)?.first as! UIView
view.frame = bounds
view.translatesAutoresizingMaskIntoConstraints = true
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
}
@IBAction private func selectKey(_ sender: UIButton) {
delegate?.customKeyboardView(self, didSelectKey: sender.titleLabel!.text!)
}
}
キーボードの高さはわりと自由みたいです![]()
サクッとカスタムキーボードが表示できました!ただカスタムキーボードを入力制限目的で利用するにはいくつか問題があるのでオススメしません
コピペとか外部キーボード接続とか。。。
コピペに関しては UITextField のカスタムクラスをつくって canPerformAction ごにょごにょすればいける気がしますが外部キーボードに関しては防げないと思われます(そもそもあんまり UITextField で入力制限なんてしない方がいいと思います)。
UIControlをカスタマイズ
とくに UITextField の inputView に設定する!でも問題ないのですが UITextField を使うとなるとレイアウトの自由度が低いのでせっかくなんでカスタムクラスをつくった方が自由度が高いのでおすすめです![]()
下記のように UIControl を継承したクラスをつくって canBecomeFirstResponder と inputView を設定してやるとカスタムキーボードが表示できます。UIView じゃないのは addAction がしたかったからです。フォーカス時がわかりにくいので枠線の色を変更しています。
protocol PickerInputControlDelegate: AnyObject {
func pickerInputControl(_ pickerInputControl: PickerInputControl, didSelectValue value: String)
}
final class PickerInputControl: UIControl {
var items = [String]()
weak var delegate: PickerInputControlDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
commonInit()
}
private func commonInit() {
layer.cornerRadius = 8.0
layer.borderWidth = 1.0
layer.borderColor = UIColor.systemGray.cgColor
addAction(.init(handler: { [weak self] _ in
self?.isSelected.toggle()
if self?.isSelected == true {
self?.becomeFirstResponder()
} else {
self?.resignFirstResponder()
}
}), for: .touchUpInside)
}
override var inputView: UIView? {
let picker = UIPickerView()
picker.delegate = self
picker.dataSource = self
return picker
}
override var canBecomeFirstResponder: Bool {
return true
}
override var isSelected: Bool {
didSet {
layer.borderColor = isSelected ? UIColor.systemBlue.cgColor : UIColor.systemGray.cgColor
}
}
@discardableResult
override func becomeFirstResponder() -> Bool {
let value = super.becomeFirstResponder()
isSelected = isFirstResponder
return value
}
@discardableResult
override func resignFirstResponder() -> Bool {
let value = super.resignFirstResponder()
isSelected = isFirstResponder
return value
}
}
extension PickerInputControl: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
delegate?.pickerInputControl(self, didSelectValue: items[row])
}
}
extension PickerInputControl: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return items.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return items[row]
}
}
こんな感じで UIControl なので中に View 置き放題です。
UIPickerViewDelegate, UIPickerViewDataSource, items は外に出した方が汎用性が高いかも![]()
static cellと組み合わせる
おまけで UITableViewController の static cell と組み合わせるとフォーカス時に自動スクロールしてくれるので複数の入力項目がある場合はおすすめです![]()
こんな感じ。
おわりに
カスタムキーボードを作る際はタップ領域をちゃんと確保してあげると素敵です。ちょっと古い記事ですが下記の記事はキーボードのタップ領域とか詳しく調べていておもしろいです。
わりとお世話になった手法だけどこれからはあんまり使わなくなるのかな UIKit。。。
調べてて気になったけどこいつ知らねえ![]()


