iOS 键盘开发

3,445 阅读7分钟

准备

1、新建扩展

image.png

选择keyboard image.png

选中targets 中的键盘target -- CustomKeyBoard

【特别注意】:设置 minimum deployments 不然的话 在低版本的设备中跑起来无效

2、设置podfile



target '主APP' do
  use_frameworks!
  pod 'Masonry'   // 引用第三方
end


// **重要** 扩展
target 'CustomKeyBoard' do
  use_frameworks!
  pod 'Masonry'   // 引用第三方
end

3、 设置键盘扩展的info.plist

plist中的字段App Extension Keys中都有解释。

PrimaryLanguage - 默认为en_US的字串。中文为:zh_CN。以<语种>_<区域>的形式描述键盘的主语言。更多内容请参考

RequestsOpenAccess 默认为NO的布尔值。是否需要比基础键盘更大的沙盒范围。把该值置为YES将需要完全访问权限,你的键盘将获得如下能力,每个能力都伴随有相应的权限:

  • 访问定位服务,通讯录数据库,相机,每个都需要用户允许
  • 与键盘的容器app共享容器数据,以便完成比如在容器app中管理用户词库的界面的功能
  • 通过网络发送按键、输入事件之类的数据供云端处理
  • 使用UIPasteboard
  • 播放音频,包括使用playInputClick方法播放按键音
  • 访问iCloud,可以用来根据用户身份同步比如键盘设置、自定义自动纠错词典
  • 通过容器app访问游戏中心和应用内购买
  • 如果你的键盘支持移动设备管理(MDM),可以与被管理的app无缝合作

image.png

完全访问权限设置为YES的时候,在设置中,才有是否开启的按钮:

image.png

4、文件引用

新建pch。在keyboard targets中引入pch文件

选中键盘的target -> Build Setting -> All -> 搜索"prefix head" -> 修改Prefix Header的内容为:

方式1项目根目录/pch文件路径/xxx.pch, 根目录是从xxx.xcworkspace同级开始。
比如GATKeyboard/路径/KeyBoardPrefixHeader.pch

image.png

方式2、在1的基础上加上$(SRCROOT)/,表示的是pch的绝对路径。$(SRCROOT)/GATKeyboard/路径/KeyBoardPrefixHeader.pch

image.png

5、报错:编译错误 Undefined symbol: OBJC_CLASS$_xxxx

引用了其他target的文件,如果确定要用,需要将相应的文件加入当前的target。

引用文件

或者:

image.png

6、引用资源无效

同上面文件的引用

7、设置键盘名称

和设置APP名称是一样的,不过设置APP名称有快捷入口: app的Target - General - Identity - display Name;
但是扩展键盘没有这个快捷入口,只能通过info.plist中设置,路径:键盘的Target -> info -> 新增Bundle display name或者CFBundleDisplayName;

这里需要注意:

1、设置的APP名称如果和APP名称一致或者是APP名称的一部分。那么在键盘的切换时,是只显示设置的名字。
比如 APP名字叫 百度输入法,设置键盘的名字是 百度, 那么切换键盘的时候展示的是 百度
2、如果设置的不是APP名称或不是其中的一部分,那么切换的时候,展示的名称是 键盘名称-APP名称
比如 APP名字叫百度输入法,设置键盘的名字是 腾讯,那么切换键盘的时候展示的是 腾讯-百度输入法

这是为了确保用户是切换键盘时,能明确知道键盘属于那个APP。 image.png

功能代码

1、设置键盘高度

//参考来源 https://cloud.tencent.com/developer/ask/sof/115790183

@property (nonatomic, assign) NSLayoutConstraint *heightConstraint;

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self updateKeyBoardHeight];
}

- (void)updateKeyBoardHeight {

    CGFloat height = 400;

    if (self.heightConstraint == nil) {
        NSLayoutConstraint *heightConstraint =
        [NSLayoutConstraint constraintWithItem: self.view
                                     attribute: NSLayoutAttributeHeight
                                     relatedBy: NSLayoutRelationEqual
                                        toItem: nil
                                     attribute: NSLayoutAttributeNotAnAttribute
                                    multiplier: 0.0
                                      constant: height];
        [self.view addConstraint: heightConstraint];
    } else {
        self.heightConstraint.constant = height;
    }
}

2、判断是否添加了键盘

在主APP中,一般需要引导用户将自定义键盘添加到键盘列表中,可以使用这样的方法

+ (BOOL)isKeyboardEnabled {
    NSArray *obj = [[NSUserDefaults standardUserDefaults] valueForKey:@"AppleKeyboards"];    ///< 这里是固定的default中的key,不用改
    if (IsEmpty(obj)) {
        return NO;
    }
    return [obj containsObject:@"你自己键盘的bundleID"];
}

3、判断是否开启了完全访问权限

在键盘扩展中


BOOL full =  self.hasFullAccess;

4、数据交互

如果需要和主APP进行数据交互。开启完全访问权限是必要的,同时,扩展和主app都需要用到的文件,需要勾选相应的target,参照上面的【编译错误】

1、开启完全访问权限

2、添加App Groups

添加App Groups

在xcode中添加Capability

image.png

配置证书

1、最简单的是通过Xcode自动添加(前提是登录了开发者账号)

2、手动添加
配置主app的bundleid和描述文件
配置扩展的bundleid和描述文件
配置app的groupid并将主app和扩展的描述文件添加AppGroups的权限

截屏2024-07-30 16.28.47.png

截屏2024-07-30 16.35.42.png

数据交换的实现

1、NSUserDefaults

通过 App Groups 共同维护 UserDefault 是一种比较简单的通讯方法。但是开发者也需要注意的是如果我们的键盘没有获取到没有完全访问权限,键盘是只能读取,没法修改 UserDefault 的值的(如果这个 UserDefault 是 Containing app 创建的)。 来源

一般来说,最简单的方式是使用userdefault来存储、读取。

// .h
+ (void)keyboardNetGender:(NSInteger)gender;  //存
+ (NSInteger)keyboardNetGender;               //取



// .m
+ (void)keyboardNetGender:(NSInteger)gender {
    [[self keyboardDefaults] setInteger:gender forKey:keyboard_netgender];
}

+ (NSInteger)keyboardNetGender {
    return [[[self keyboardDefaults] valueForKey:keyboard_netgender] integerValue];
}

+ (NSUserDefaults *)keyboardDefaults {
    return [[NSUserDefaults alloc] initWithSuiteName:@"你的groupID"];  
    
    //这里的id要注意,就是上面手动配置证书步骤中的groupid
}
2、LKDB交换 不行

数据交换目前只用了userdefault有效,也尝试使用了LKDB(FMDB的封装),发现数据不能进行交互,仔细一一想,FMDB是将数据存储到APP的沙盒内,而键盘和主APP是2个独立的,不能访问另一个沙盒的内容。

生命周期

键盘扩展是继承ViewController,所以其生命周期遵循ViewController。

viewDidLoad
viewWillAppear
viewDidAppear
viewWillDisappear
viewDidDisappear
dealloc

在实际测试中,存在一些异常

1、切换键盘

调用顺序

----------------1   obj:<KeyboardViewController: 0x104d0aac0>    -[KeyboardViewController viewWillAppear:]

----------------2   obj:<KeyboardViewController: 0x104d0aac0>    -[KeyboardViewController viewDidAppear:]

// 操作 -------------------------切换成系统键盘

----------------3   obj:<KeyboardViewController: 0x104d0aac0>    -[KeyboardViewController viewWillDisappear:]

----------------5   obj:<KeyboardViewController: 0x104d0aac0>    -[KeyboardViewController dealloc]

// 操作 -------------------------切换成自己的键盘

----------------1   obj:<KeyboardViewController: 0x104e1b900>    -[KeyboardViewController viewWillAppear:]

----------------2   obj:<KeyboardViewController: 0x104e1b900>    -[KeyboardViewController viewDidAppear:]

// 操作 -------------------------切换成系统键盘

----------------3   obj:<KeyboardViewController: 0x104e1b900>    -[KeyboardViewController viewWillDisappear:]

----------------4   obj:<KeyboardViewController: 0x104e1b900>    -[KeyboardViewController viewDidDisappear:]

----------------5   obj:<KeyboardViewController: 0x104e1b900>    -[KeyboardViewController dealloc]**

// 操作 -------------------------切换成自己的键盘

----------------1   obj:<KeyboardViewController: 0x101a294d0>    -[KeyboardViewController viewWillAppear:]

----------------2   obj:<KeyboardViewController: 0x101a294d0>    -[KeyboardViewController viewDidAppear:]

// 操作 -------------------------切换成系统键盘

----------------3   obj:<KeyboardViewController: 0x101a294d0>    -[KeyboardViewController viewWillDisappear:]

----------------5   obj:<KeyboardViewController: 0x101a294d0>    -[KeyboardViewController dealloc]

// 操作 -------------------------切换成自己的键盘

----------------1   obj:<KeyboardViewController: 0x104f4c1a0>    -[KeyboardViewController viewWillAppear:]

----------------2   obj:<KeyboardViewController: 0x104f4c1a0>    -[KeyboardViewController viewDidAppear:]

// 操作 -------------------------切换成系统键盘

----------------3   obj:<KeyboardViewController: 0x104f4c1a0>    -[KeyboardViewController viewWillDisappear:]

----------------5   obj:<KeyboardViewController: 0x104f4c1a0>    -[KeyboardViewController dealloc]

在这些打印中能发现
1、viewWillAppear、viewDidAppear、viewWillDisappear 这三个都是正常
2、viewDidDisappear 有些时候不会被调用
3、dealloc 是正常的

2、键盘消失(输入框失去第一响应者)

调用顺序


// 操作 -------------------------成为第一响应者

 ----------------1   obj:<KeyboardViewController: 0x10350d4d0>    -[KeyboardViewController viewWillAppear:]
 ----------------2   obj:<KeyboardViewController: 0x10350d4d0>    -[KeyboardViewController viewDidAppear:]

// 操作 ------------失去焦点(键盘消失)

 ----------------3   obj:<KeyboardViewController: 0x10350d4d0>    -[KeyboardViewController viewWillDisappear:]
 ----------------4   obj:<KeyboardViewController: 0x10350d4d0>    -[KeyboardViewController viewDidDisappear:]
 ----------------3   obj:<KeyboardViewController: 0x10350d4d0>    -[KeyboardViewController viewWillDisappear:]
 ----------------5   obj:<KeyboardViewController: 0x10350d4d0>    -[KeyboardViewController dealloc]

// 操作 -------------------------成为第一响应者

 ----------------1   obj:<KeyboardViewController: 0x103d08e20>    -[KeyboardViewController viewWillAppear:]
 ----------------2   obj:<KeyboardViewController: 0x103d08e20>    -[KeyboardViewController viewDidAppear:]

// 操作 ------------失去焦点(键盘消失)

 ----------------3   obj:<KeyboardViewController: 0x103d08e20>    -[KeyboardViewController viewWillDisappear:]
 ----------------4   obj:<KeyboardViewController: 0x103d08e20>    -[KeyboardViewController viewDidDisappear:]
 ----------------3   obj:<KeyboardViewController: 0x103d08e20>    -[KeyboardViewController viewWillDisappear:]
 ----------------5   obj:<KeyboardViewController: 0x103d08e20>    -[KeyboardViewController dealloc]

// 操作 -------------------------成为第一响应者

 ----------------1   obj:<KeyboardViewController: 0x106f074c0>    -[KeyboardViewController viewWillAppear:]
 ----------------2   obj:<KeyboardViewController: 0x106f074c0>    -[KeyboardViewController viewDidAppear:]

// 操作 ------------失去焦点(键盘消失)

 ----------------3   obj:<KeyboardViewController: 0x106f074c0>    -[KeyboardViewController viewWillDisappear:]
 ----------------4   obj:<KeyboardViewController: 0x106f074c0>    -[KeyboardViewController viewDidDisappear:]
 ----------------3   obj:<KeyboardViewController: 0x106f074c0>    -[KeyboardViewController viewWillDisappear:]
 ----------------5   obj:<KeyboardViewController: 0x106f074c0>    -[KeyboardViewController dealloc]

// 操作 -------------------------成为第一响应者

 ----------------1   obj:<KeyboardViewController: 0x106e0bd90>    -[KeyboardViewController viewWillAppear:]
 ----------------2   obj:<KeyboardViewController: 0x106e0bd90>    -[KeyboardViewController viewDidAppear:]

// 操作 ------------失去焦点(键盘消失)

 ----------------3   obj:<KeyboardViewController: 0x106e0bd90>    -[KeyboardViewController viewWillDisappear:]
 ----------------4   obj:<KeyboardViewController: 0x106e0bd90>    -[KeyboardViewController viewDidDisappear:]
 ----------------3   obj:<KeyboardViewController: 0x106e0bd90>    -[KeyboardViewController viewWillDisappear:]
 ----------------5   obj:<KeyboardViewController: 0x106e0bd90>    -[KeyboardViewController dealloc]


// 操作 -------------------------成为第一响应者

 ----------------1   obj:<KeyboardViewController: 0x106e136c0>    -[KeyboardViewController viewWillAppear:]
 ----------------2   obj:<KeyboardViewController: 0x106e136c0>    -[KeyboardViewController viewDidAppear:]

// 操作 ------------失去焦点(键盘消失)

 ----------------3   obj:<KeyboardViewController: 0x106e136c0>    -[KeyboardViewController viewWillDisappear:]
 ----------------4   obj:<KeyboardViewController: 0x106e136c0>    -[KeyboardViewController viewDidDisappear:]
 ----------------3   obj:<KeyboardViewController: 0x106e136c0>    -[KeyboardViewController viewWillDisappear:]
 ----------------5   obj:<KeyboardViewController: 0x106e136c0>    -[KeyboardViewController dealloc]


// 操作 -------------------------成为第一响应者

 ----------------1   obj:<KeyboardViewController: 0x106e13d80>    -[KeyboardViewController viewWillAppear:]
 ----------------2   obj:<KeyboardViewController: 0x106e13d80>    -[KeyboardViewController viewDidAppear:]

// 操作 ------------失去焦点(键盘消失)

 ----------------3   obj:<KeyboardViewController: 0x106e13d80>    -[KeyboardViewController viewWillDisappear:]
 ----------------4   obj:<KeyboardViewController: 0x106e13d80>    -[KeyboardViewController viewDidDisappear:]
 ----------------3   obj:<KeyboardViewController: 0x106e13d80>    -[KeyboardViewController viewWillDisappear:]
 ----------------5   obj:<KeyboardViewController: 0x106e13d80>    -[KeyboardViewController dealloc]

在这些打印中能发现
1、viewWillAppear、viewDidAppear 这2个都是正常
2、viewDidDisappear 也是正常被调用
3、viewWillDisappear 会被多次调用
4、dealloc 是正常的

从上面的2个操作中能看出:viewWillDisappear 和 viewDidDisappear 在不同的操作中,生命周期并没有按照我们预期的走