Swift 数据输入自定义视图示例(自定义应用内键盘)
A Swift example of Custom Views for Data Input (custom in-app keyboard)
目标
我想制作一个只在我的应用程序中使用的自定义键盘,而不是需要安装的系统键盘。
我阅读和尝试过的内容
文档
上面第一篇文章指出:
Make sure a custom, systemwide keyboard is indeed what you want to
develop. To provide a fully custom keyboard for just your app or to
supplement the system keyboard with custom keys in just your app, the
iOS SDK provides other, better options. Read about custom input views
and input accessory views in Custom Views for Data Input in Text
Programming Guide for iOS.
这就是让我看到上面第二篇文章的原因。但是,那篇文章没有足够的细节让我开始。
教程
- iOS 8: Creating a Custom Keyboard in Swift
- How to make a custom keyboard in iOS 8 using Swift
- Xcode 6 Tutorial: iOS 8.0 Simple Custom Keyboard in Swift
- Creating a Custom Keyboard Using iOS 8 App Extension
我能够从上面列表中的第二个教程中获得一个可用的键盘。但是,我找不到任何教程来说明如何制作 Custom Views for Data Input 文档中所述的仅应用内键盘。
堆栈溢出
我在回答当前问题的过程中也问了(并回答了)这些问题。
- Delegates in Swift
问题
有没有人有应用内自定义键盘的最小示例(甚至只有一个按钮)?我不是在寻找一个完整的教程,只是一个我可以自己扩展的概念证明。
这是一个基本的应用程序内键盘。几乎可以使用相同的方法来制作任何键盘布局。以下是需要完成的主要事项:
- 在 .xib 文件中创建键盘布局,其所有者是包含
UIView
subclass. 的 .swift 文件
- 告诉
UITextField
使用自定义键盘。
- 使用委托在键盘和主视图控制器之间进行通信。
创建 .xib 键盘布局文件
- 在 Xcode 中转到 文件 > 新建 > 文件... > iOS > 用户界面 > 查看 以创建 .xib 文件。
- 我叫我的Keyboard.xib
- 添加您需要的按钮。
- 使用自动布局约束,这样无论键盘大小如何,按钮都会相应地调整大小。
- 将文件所有者(不是根视图)设置为 Keyboard.swift 文件。这是一个常见的错误来源。见末尾的注释。
创建 .swift UIView 子class 键盘文件
- 在 Xcode 中转到 文件 > 新建 > 文件... > iOS > 源 > Cocoa 触摸 Class 创建 .swift 文件。
- 我叫我的Keyboard.swift
添加以下代码:
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
方法,将它们全部挂钩。
- 注意协议和委托代码。有关委托如何工作的简单说明,请参阅 this answer。
设置视图控制器
- 将
UITextField
添加到您的主故事板,并使用 IBOutlet
将其连接到您的视图控制器。称之为 textField
.
为视图控制器使用以下代码:
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 错误,可能是因为您将视图的自定义 class 设置为 Keyboard.swift 而不是为 nib 文件的所有者执行此操作。
Select Keyboard.nib 然后选择文件所有者。
确保根视图的自定义 class 为空。
基于 Suragch 的回答,我需要一个完成和退格按钮,如果你像我一样是菜鸟,这里有一些你可能会遇到的错误以及我解决它们的方法。
出现 EXC_BAD_ACCESS 错误?
我包括:
@objc(classname)
class classname: UIView{
}
解决了我的问题,但 Suragch 的更新答案似乎更 appropriate/correct 解决了这个问题。
收到 SIGABRT 错误?
另一个愚蠢的事情是以错误的方式拖动连接,导致 SIGABRT 错误。不要从函数拖到按钮,而是将按钮拖到函数。
添加完成按钮
我在 keyboard.swift:
中将其添加到协议中
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
}
然后将一个新的 IBAction 从我的完成按钮连接到 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()
}
其次: 连接一个类似于 Done 动作的新 IBAction:
@IBAction func backspace(sender: UIButton) {
self.delegate?.backspace()
}
第三: 转到出现 NumberPad 的 viewController.swift。
重要提示: 在 viewDidLoad() 中设置将使用此键盘的所有文本字段。所以你的 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
}
我不确定如何,如果有一种方法可以对视图中的所有文本字段执行此操作。这会很方便...
第四: 还是在viewController.swift中我们需要添加一个变量和两个函数。它看起来像这样:
var activeTextField = UITextField()
func textFieldDidBeginEditing(textField: UITextField) {
print("Setting Active Textfield")
self.activeTextField = textField
print("Active textField Set!")
}
func backspace() {
print("backspaced!")
activeTextField.deleteBackward()
}
这里发生的事情的解释:
- 您创建一个变量来保存文本字段。
- 当调用 "textFieldDidBeginEditing" 时,它会设置变量,以便它知道我们正在处理哪个文本字段。我添加了很多 prints() 所以我们知道一切都在执行。
- 然后我们的退格函数检查我们正在处理的文本字段并使用 .deleteBackward()。这将删除光标前的直接字符。
而且你应该在做生意。
非常感谢 Suragchs 帮助我实现了这一目标。
关键是要用现有的UIKeyInput
protocol, to which UITextField
already conforms. Then your keyboard view need only to send insertText()
and deleteBackward()
来控制
以下示例创建自定义数字键盘:
class DigitButton: UIButton {
var digit: Int = 0
}
class NumericKeyboard: UIView {
weak var target: (UIKeyInput & UITextInput)?
var useDecimalSeparator: Bool
var numericButtons: [DigitButton] = (0...9).map {
let button = DigitButton(type: .system)
button.digit = [=10=]
button.setTitle("\([=10=])", for: .normal)
button.titleLabel?.font = .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 = .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
}()
lazy var decimalButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = Locale.current.decimalSeparator ?? "."
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapDecimalButton(_:)), for: .touchUpInside)
return button
}()
init(target: UIKeyInput & UITextInput, useDecimalSeparator: Bool = false) {
self.target = target
self.useDecimalSeparator = useDecimalSeparator
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) {
insertText("\(sender.digit)")
}
@objc func didTapDecimalButton(_ sender: DigitButton) {
insertText(Locale.current.decimalSeparator ?? ".")
}
@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)
if useDecimalSeparator {
subStackView.addArrangedSubview(decimalButton)
} else {
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
}
func insertText(_ string: String) {
guard let range = target?.selectedRange else { return }
if let textField = target as? UITextField, textField.delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == false {
return
}
if let textView = target as? UITextView, textView.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: string) == false {
return
}
target?.insertText(string)
}
}
// MARK: - UITextInput extension
extension UITextInput {
var selectedRange: NSRange? {
guard let textRange = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: textRange.start)
let length = offset(from: textRange.start, to: textRange.end)
return NSRange(location: location, length: length)
}
}
那么你可以:
textField.inputView = NumericKeyboard(target: textField)
产生:
或者,如果您也需要小数点分隔符,您可以:
textField.inputView = NumericKeyboard(target: textField, useDecimalSeparator: true)
以上是相当原始的,但它说明了这个想法:制作你自己的输入视图并使用 UIKeyInput
协议将键盘输入传递给控件。
另请注意使用 accessibilityTraits
以获得正确的“语音内容”»“朗读屏幕”行为。如果您为按钮使用图像,请务必也设置 accessibilityLabel
。
目标
我想制作一个只在我的应用程序中使用的自定义键盘,而不是需要安装的系统键盘。
我阅读和尝试过的内容
文档
上面第一篇文章指出:
Make sure a custom, systemwide keyboard is indeed what you want to develop. To provide a fully custom keyboard for just your app or to supplement the system keyboard with custom keys in just your app, the iOS SDK provides other, better options. Read about custom input views and input accessory views in Custom Views for Data Input in Text Programming Guide for iOS.
这就是让我看到上面第二篇文章的原因。但是,那篇文章没有足够的细节让我开始。
教程
- iOS 8: Creating a Custom Keyboard in Swift
- How to make a custom keyboard in iOS 8 using Swift
- Xcode 6 Tutorial: iOS 8.0 Simple Custom Keyboard in Swift
- Creating a Custom Keyboard Using iOS 8 App Extension
我能够从上面列表中的第二个教程中获得一个可用的键盘。但是,我找不到任何教程来说明如何制作 Custom Views for Data Input 文档中所述的仅应用内键盘。
堆栈溢出
我在回答当前问题的过程中也问了(并回答了)这些问题。
- Delegates in Swift
问题
有没有人有应用内自定义键盘的最小示例(甚至只有一个按钮)?我不是在寻找一个完整的教程,只是一个我可以自己扩展的概念证明。
这是一个基本的应用程序内键盘。几乎可以使用相同的方法来制作任何键盘布局。以下是需要完成的主要事项:
- 在 .xib 文件中创建键盘布局,其所有者是包含
UIView
subclass. 的 .swift 文件
- 告诉
UITextField
使用自定义键盘。 - 使用委托在键盘和主视图控制器之间进行通信。
创建 .xib 键盘布局文件
- 在 Xcode 中转到 文件 > 新建 > 文件... > iOS > 用户界面 > 查看 以创建 .xib 文件。
- 我叫我的Keyboard.xib
- 添加您需要的按钮。
- 使用自动布局约束,这样无论键盘大小如何,按钮都会相应地调整大小。
- 将文件所有者(不是根视图)设置为 Keyboard.swift 文件。这是一个常见的错误来源。见末尾的注释。
创建 .swift UIView 子class 键盘文件
- 在 Xcode 中转到 文件 > 新建 > 文件... > iOS > 源 > Cocoa 触摸 Class 创建 .swift 文件。
- 我叫我的Keyboard.swift
添加以下代码:
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
方法,将它们全部挂钩。- 注意协议和委托代码。有关委托如何工作的简单说明,请参阅 this answer。
设置视图控制器
- 将
UITextField
添加到您的主故事板,并使用IBOutlet
将其连接到您的视图控制器。称之为textField
. 为视图控制器使用以下代码:
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 错误,可能是因为您将视图的自定义 class 设置为 Keyboard.swift 而不是为 nib 文件的所有者执行此操作。
Select Keyboard.nib 然后选择文件所有者。
确保根视图的自定义 class 为空。
基于 Suragch 的回答,我需要一个完成和退格按钮,如果你像我一样是菜鸟,这里有一些你可能会遇到的错误以及我解决它们的方法。
出现 EXC_BAD_ACCESS 错误? 我包括:
@objc(classname)
class classname: UIView{
}
解决了我的问题,但 Suragch 的更新答案似乎更 appropriate/correct 解决了这个问题。
收到 SIGABRT 错误? 另一个愚蠢的事情是以错误的方式拖动连接,导致 SIGABRT 错误。不要从函数拖到按钮,而是将按钮拖到函数。
添加完成按钮 我在 keyboard.swift:
中将其添加到协议中protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
}
然后将一个新的 IBAction 从我的完成按钮连接到 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()
}
其次: 连接一个类似于 Done 动作的新 IBAction:
@IBAction func backspace(sender: UIButton) {
self.delegate?.backspace()
}
第三: 转到出现 NumberPad 的 viewController.swift。
重要提示: 在 viewDidLoad() 中设置将使用此键盘的所有文本字段。所以你的 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
}
我不确定如何,如果有一种方法可以对视图中的所有文本字段执行此操作。这会很方便...
第四: 还是在viewController.swift中我们需要添加一个变量和两个函数。它看起来像这样:
var activeTextField = UITextField()
func textFieldDidBeginEditing(textField: UITextField) {
print("Setting Active Textfield")
self.activeTextField = textField
print("Active textField Set!")
}
func backspace() {
print("backspaced!")
activeTextField.deleteBackward()
}
这里发生的事情的解释:
- 您创建一个变量来保存文本字段。
- 当调用 "textFieldDidBeginEditing" 时,它会设置变量,以便它知道我们正在处理哪个文本字段。我添加了很多 prints() 所以我们知道一切都在执行。
- 然后我们的退格函数检查我们正在处理的文本字段并使用 .deleteBackward()。这将删除光标前的直接字符。
而且你应该在做生意。 非常感谢 Suragchs 帮助我实现了这一目标。
关键是要用现有的UIKeyInput
protocol, to which UITextField
already conforms. Then your keyboard view need only to send insertText()
and deleteBackward()
来控制
以下示例创建自定义数字键盘:
class DigitButton: UIButton {
var digit: Int = 0
}
class NumericKeyboard: UIView {
weak var target: (UIKeyInput & UITextInput)?
var useDecimalSeparator: Bool
var numericButtons: [DigitButton] = (0...9).map {
let button = DigitButton(type: .system)
button.digit = [=10=]
button.setTitle("\([=10=])", for: .normal)
button.titleLabel?.font = .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 = .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
}()
lazy var decimalButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = Locale.current.decimalSeparator ?? "."
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapDecimalButton(_:)), for: .touchUpInside)
return button
}()
init(target: UIKeyInput & UITextInput, useDecimalSeparator: Bool = false) {
self.target = target
self.useDecimalSeparator = useDecimalSeparator
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) {
insertText("\(sender.digit)")
}
@objc func didTapDecimalButton(_ sender: DigitButton) {
insertText(Locale.current.decimalSeparator ?? ".")
}
@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)
if useDecimalSeparator {
subStackView.addArrangedSubview(decimalButton)
} else {
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
}
func insertText(_ string: String) {
guard let range = target?.selectedRange else { return }
if let textField = target as? UITextField, textField.delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == false {
return
}
if let textView = target as? UITextView, textView.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: string) == false {
return
}
target?.insertText(string)
}
}
// MARK: - UITextInput extension
extension UITextInput {
var selectedRange: NSRange? {
guard let textRange = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: textRange.start)
let length = offset(from: textRange.start, to: textRange.end)
return NSRange(location: location, length: length)
}
}
那么你可以:
textField.inputView = NumericKeyboard(target: textField)
产生:
或者,如果您也需要小数点分隔符,您可以:
textField.inputView = NumericKeyboard(target: textField, useDecimalSeparator: true)
以上是相当原始的,但它说明了这个想法:制作你自己的输入视图并使用 UIKeyInput
协议将键盘输入传递给控件。
另请注意使用 accessibilityTraits
以获得正确的“语音内容”»“朗读屏幕”行为。如果您为按钮使用图像,请务必也设置 accessibilityLabel
。