创建自定义应用内键盘

http://i.stack.imgur.com/10lPa.gif

这是一个基本的应用内键盘。可以使用相同的方法来制作任何键盘布局。以下是需要完成的主要工作:

  • 在 .xib 文件中创建键盘布局,该文件的所有者是 Swift 或 Objective-C 类,它是 UIView 的子类。
  • 告诉 UITextField 使用自定义键盘。
  • 使用委托在键盘和主视图控制器之间进行通信。

创建 .xib 键盘布局文件

  • 在 Xcode 中,转到文件>新建>文件…> iOS>用户界面>视图以创建 .xib 文件。
  • 我调用我的 Keyboard.xib
  • 添加所需的按钮。
  • 使用自动布局约束,这样无论键盘的大小如何,按钮都会相应调整大小。
  • 将文件所有者(不是根视图)设置为 Keyboard 类。这是一个常见的错误来源。你将在下一步中创建此类。请参阅最后的注释。

创建 .swift UIView 子类键盘文件

  • 在 Xcode 中,转到 File> New> File …> iOS> Source> Cocoa Touch Class 来创建 Swift 或 Objective-C 类。选择 UIView 作为新创建的类的超类

  • 我调用我的 Keyboard.swift(Objective-C 中的 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 = NSBundle.mainBundle().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(sender.titleLabel!.text!) // could alternatively send a tag value
          }
    
      }
    
  • 为 Objective-C 添加以下代码:

    Keyboard.h 文件

    #import <UIKit/UIKit.h>
    
    // The view controller will adopt this protocol (delegate)
    // and thus must contain the keyWasTapped method
    @protocol KeyboardDelegate<NSObject>
    - (void)keyWasTapped:(NSString *)character;
    @end
    
    @interface Keyboard : UIView
    @property (nonatomic, weak) id<KeyboardDelegate> delegate;  
    @end
    

    Keyboard.m 文件

    #import "Keyboard.h"
    
    @implementation Keyboard
    
    - (id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        [self initializeSubviews];
        return self;
    }
    
    - (id)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        [self initializeSubviews];
        return self;
    }
    
    - (void)initializeSubviews {
        NSString *xibFileName = @"Keyboard"; // xib extention not included
        UIView *view = [[[NSBundle mainBundle] loadNibNamed:xibFileName owner:self options:nil] firstObject];
        [self addSubview:view];
        view.frame = self.bounds;
    }
    
    // MARK:- Button actions from .xib file
    
    -(IBAction)keyTapped:(UIButton *)sender {
        // When a button is tapped, send that information to the
        // delegate (ie, the view controller)
        [self.delegate keyWasTapped:sender.titleLabel.text]; // could alternatively send a tag value
    }
    
    @end
    
  • 控制从 .xib 文件中的按钮到按钮回调的拖动操作到 Swift 或 Objective-C 所有者中的 @IBAction 方法,以将它们全部挂起。

  • 请注意协议和委托代码。有关代表如何工作的简单说明,请参阅此答案

设置视图控制器

  • UITextField 添加到主故事板并使用 IBOutlet 将其连接到视图控制器。叫它 textField

  • 在 Swift 中对 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)
          }
      }
    
  • 对 Objective-C 使用以下代码:

    .h 文件

    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController
    
    @end
    

    .m 文件

    #import "ViewController.h"
    #import "Keyboard.h"
    
    @interface ViewController ()<KeyboardDelegate>
    
    @property (nonatomic, weak) IBOutlet UITextField *textField;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    
        // initialize custom keyboard
        Keyboard *keyboardView = [[Keyboard alloc] initWithFrame:CGRectMake(0, 0, 0, 300)];
        keyboardView.delegate = self; // the view controller will be notified by the keyboard whenever a key is tapped
    
        // replace system keyboard with custom keyboard
        self.textField.inputView = keyboardView;
    }
    
    - (void)keyWasTapped:(NSString *)character {
        [self.textField insertText:character];
    }
    
    @end
    
  • 请注意,视图控制器采用我们在上面定义的 KeyboardDelegate 协议。

常见错误

如果你收到 EXC_BAD_ACCESS 错误,可能是因为你将视图的自定义类设置为 Keyboard 而不是为 nib 文件的所有者执行此操作。

选择 Keyboard.nib,然后选择 File’s Owner。

StackOverflow 文档

确保根视图的自定义类为空。

StackOverflow 文档

笔记

此示例最初来自此 Stack Overflow 答案