インストールする必要があるシステムキーボードではなく、アプリ内でのみ使用されるカスタムキーボードを作成したい。
ドキュメント
上記の最初の記事には次のように記載されています。
カスタムのシステム全体のキーボードが実際に開発したいものであることを確認してください。アプリ専用の完全カスタムキーボードを提供したり、アプリ専用のカスタムキーでシステムキーボードを補完したりするために、iOS SDKには他の優れたオプションが用意されています。 iOS用テキストプログラミングガイドのデータ入力のカスタムビューで、カスタム入力ビューと入力アクセサリビューについてお読みください。
それが私を上記の2番目の記事に導いたものです。しかし、その記事には、始めるのに十分な詳細がありませんでした。
チュートリアル
上記のリストの2番目のチュートリアルから、機能するキーボードを取得できました。ただし、 データ入力のカスタムビュー のドキュメントで説明されているように、アプリ内のみのキーボードを作成する方法を示すチュートリアルは見つかりませんでした。
スタックオーバーフロー
また、現在の質問に答える途中でこれらの質問をしました(そして答えました)。
アプリ内のカスタムキーボードの最小限の例(ボタンが1つでもある)はありますか?チュートリアル全体を探しているのではなく、自分自身で拡張できる概念の証明を探しているだけです。
重要なのは、UIKeyInput
が既に準拠している既存の UITextField
プロトコルを使用することです。次に、キーボードビューは insertText()
および deleteBackward()
をコントロールに送信するだけです。
次の例は、カスタム数値キーボードを作成します。
class DigitButton: UIButton {
var digit: Int = 0
}
class NumericKeyboard: UIView {
weak var target: UIKeyInput?
var numericButtons: [DigitButton] = (0...9).map {
let button = DigitButton(type: .system)
button.digit = $0
button.setTitle("\($0)", for: .normal)
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.addTarget(self, action: #selector(didTapDigitButton(_:)), for: .touchUpInside)
return button
}
var deleteButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("⌫", for: .normal)
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = "Delete"
button.addTarget(self, action: #selector(didTapDeleteButton(_:)), for: .touchUpInside)
return button
}()
init(target: UIKeyInput) {
self.target = target
super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Actions
extension NumericKeyboard {
@objc func didTapDigitButton(_ sender: DigitButton) {
target?.insertText("\(sender.digit)")
}
@objc func didTapDeleteButton(_ sender: DigitButton) {
target?.deleteBackward()
}
}
// MARK: - Private initial configuration methods
private extension NumericKeyboard {
func configure() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
addButtons()
}
func addButtons() {
let stackView = createStackView(axis: .vertical)
stackView.frame = bounds
stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(stackView)
for row in 0 ..< 3 {
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
for column in 0 ..< 3 {
subStackView.addArrangedSubview(numericButtons[row * 3 + column + 1])
}
}
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
let blank = UIView()
blank.layer.borderWidth = 0.5
blank.layer.borderColor = UIColor.darkGray.cgColor
subStackView.addArrangedSubview(blank)
subStackView.addArrangedSubview(numericButtons[0])
subStackView.addArrangedSubview(deleteButton)
}
func createStackView(axis: NSLayoutConstraint.Axis) -> UIStackView {
let stackView = UIStackView()
stackView.axis = axis
stackView.alignment = .fill
stackView.distribution = .fillEqually
return stackView
}
}
その後、次のことができます。
textField.inputView = NumericKeyboard(target: textField)
その結果:
上記はかなり原始的ですが、アイデアを示しています:入力ビューを所有し、UIKeyInput
プロトコルを使用してキーボード入力をコントロールに伝えます。
また、accessibilityTraits
を使用して正しい「音声コンテンツ」"「画面を話す」動作を取得することにも注意してください。また、ボタンに画像を使用する場合は、accessibilityLabel
も必ず設定してください。
これは基本的なアプリ内キーボードです。同じ方法を使用して、ほぼすべてのキーボードレイアウトを作成できます。実行する必要がある主なものは次のとおりです。
UIView
サブクラスを含む.Swiftファイルである.xibファイルにキーボードレイアウトを作成します。UITextField
にカスタムキーボードを使用するように伝えます。次のコードを追加します。
import UIKit
// The view controller will adopt this protocol (delegate)
// and thus must contain the keyWasTapped method
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
}
class Keyboard: UIView {
// This variable will be set as the view controller so that
// the keyboard can send messages to the view controller.
weak var delegate: KeyboardDelegate?
// MARK:- keyboard initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeSubviews()
}
func initializeSubviews() {
let xibFileName = "Keyboard" // xib extention not included
let view = Bundle.main.loadNibNamed(xibFileName, owner: self, options: nil)![0] as! UIView
self.addSubview(view)
view.frame = self.bounds
}
// MARK:- Button actions from .xib file
@IBAction func keyTapped(sender: UIButton) {
// When a button is tapped, send that information to the
// delegate (ie, the view controller)
self.delegate?.keyWasTapped(character: sender.titleLabel!.text!) // could alternatively send a tag value
}
}
.xibファイルのボタンから.Swiftファイルの@IBAction
メソッドへのドラッグを制御して、それらをすべてフックします。
UITextField
を追加し、IBOutlet
を使用してView Controllerに接続します。 textField
と呼びます。View Controllerに次のコードを使用します。
import UIKit
class ViewController: UIViewController, KeyboardDelegate {
@IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// initialize custom keyboard
let keyboardView = Keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: 300))
keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped
// replace system keyboard with custom keyboard
textField.inputView = keyboardView
}
// required method for keyboard delegate protocol
func keyWasTapped(character: String) {
textField.insertText(character)
}
}
View Controllerは、上記で定義したKeyboardDelegate
プロトコルを採用していることに注意してください。
EXC_BAD_ACCESSエラーが発生している場合、おそらく、nibファイルの所有者に対してこれを行うのではなく、ビューのカスタムクラスをKeyboard.Swiftとして設定しているためです。
Keyboard.nibを選択し、ファイルの所有者を選択します。
ルートビューのカスタムクラスが空白であることを確認してください。
Suragchの答えに基づいて、完了ボタンとバックスペースボタンが必要でした。あなたが私のような初心者であれば、遭遇する可能性のあるエラーとその解決方法がここにあります。
EXC_BAD_ACCESSエラーを取得しますか?含まれています:
@objc(classname)
class classname: UIView{
}
私の問題を修正しましたが、Suragchの更新された答えはこれをより適切/正しい方法で解決しているようです。
Getting SIGABRT Error?もう1つの愚かなことは、接続を間違った方向にドラッグして、SIGABRTエラーを引き起こしていました。関数からボタンにドラッグするのではなく、ボタンを関数にドラッグします。
完了ボタンの追加これをkeyboard.Swiftのプロトコルに追加しました:
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
}
次に、新しいIBActionをdoneボタンからkeyboard.Swiftに接続しました。
@IBAction func Done(sender: UIButton) {
self.delegate?.keyDone()
}
そして、このキーボードを使用しているviewController.Swiftに戻ってジャンプし、関数keyWasTappedの後にこれを追加しました:
func keyDone() {
view.endEditing(true)
}
バックスペースの追加 viewDidLoad()メソッド(後述)でtextField.delegateをselfに設定する必要があるため、これは非常につまずきました。
最初:keyboard.Swiftでプロトコルfunc backspace()に追加します:
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
func backspace()
}
Second:Doneアクションに似た新しいIBActionを接続します:
@IBAction func backspace(sender: UIButton) {
self.delegate?.backspace()
}
3番目:NumberPadが表示されるviewController.Swiftへ。
重要:viewDidLoad()で、このキーボードを使用するすべてのtextFieldsを設定します。したがって、viewDidLoad()は次のようになります。
override func viewDidLoad() {
super.viewDidLoad()
self.myTextField1.delegate = self
self.myTextField2.delegate = self
// initialize custom keyboard
let keyboardView = keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: 240))
keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped
// replace system keyboard with custom keyboard
myTextField1.inputView = keyboardView
myTextField2.inputView = keyboardView
}
ビュー内にあるすべてのtextFieldに対してこれを行う方法がある場合、どのようにすればよいのかわかりません。これは便利でしょう...
Forth:まだviewController.Swiftに変数と2つの関数を追加する必要があります。次のようになります。
var activeTextField = UITextField()
func textFieldDidBeginEditing(textField: UITextField) {
print("Setting Active Textfield")
self.activeTextField = textField
print("Active textField Set!")
}
func backspace() {
print("backspaced!")
activeTextField.deleteBackward()
}
ここで起こっていることの説明:
そして、あなたはビジネスをするべきです。これを実現させてくれたSuragchsに感謝します。