来自产品经理的"简单"需求一则
需求:在输入身份证号码的时候,弹出来的键盘是能够切换到字母的九宫格数字键盘。(左边原生系统,右边需求)
步骤一:添加键盘监控
系统提供了notification监听键盘的出现和消失。Notification不仅可以监听键盘的状态变化,还携带一些其它信息,键盘的位置,大小信息,动画类型等。
步骤二:处理监听事件
在回调函数我们首先需要找到的是当前键盘所在在window
[[UIApplication sharedApplication] windows]能够获取当前界面windows的数组。
系统键盘是在一个系统新建的级别最高的UIWindow上,我们只需要找到这个UIWindow 就可以,在这个window 上添加button,因此我们要的keyboard所在的UIWindow的名称是UITextEffectsWindow。找到我们所需要的window之后,直接添加我们的button。在调试的过程中发现,当前设置只在iOS9以下的系统起作用,在iOS9系统中添加的button会被keyboard所在的window覆盖掉。这是为什么呢,再打印当前的windows发现,当前的界面windows的结构已经与之前不一样了。
对比结构发现在iOS9的系统中多了一个UIRemoteKeyboardWindow,也就是现在最高级别的UIWindow是UIRemoteKeyboardWindow。因此keyboard所在的UIWindow也由之前的UITextEffectsWindow转变为UIRemoteKeyboardWindow。
步骤三:处理第三方键盘
除了要适配iOS9的系统的情况下,还存在另外一个问题。苹果在iOS8及以上的系统支持第三方输入法(搜狗输入法),现在很多人都下载使用了第三方的输入法 ,会出现什么问题。
自定义的控件出现在不该出现的位置,这个不是直接设置button的hidden属性为YES呢,不就解决问题了?所以关键问题如何判断当前输入法是否带有第三方扩展输入法?解决的办法有几种:
禁用第三方输入法
无论有没有使用第三方输入法,在APP中有关键盘的场景都禁用。一了百了,简单粗暴。
查找第三方输入法
第一种方法有点不太友好。我们需要的是有第三方输入法符合要求就使用第三方,没有的就用采用自定义的。 如何判断当前用户是否使用了第三方输入法,如果使用了第三方输入法 就设置button的hidden属性为YES。UITextInputMode:能够得到当前系统的所有输入法。[UITextInputModeactiveInputModes]得到其中的一个NSArray的数组。对比下在有第三方输入法和没有情况下数组的成员:
通过对比发现在有使用第三方输入法扩展的时候就多了一个UIKeyboardExtensionInputMode,通过这个mode来判断是否使用了输入法扩展。这个mode包含的第三方输入法的其它信息,版本号,或者通过keyValue来读取输入法名称。
当然我们只需要判断当前是否有引进第三方输入法。如果有需要也可以通过条件过滤获取当前第三方输入法。
私有API
也有通过私有APP来获取第三方输入法的信息,但是现在苹果对使用私有API审核很严格,还是不建议了。
2016.7.11 更新
项目上线之后,有用户反馈贴上的“ABC”这个按钮不会出现。随机概率事件,不是必先的bug无法找到路径,再review了代码之后并没有找到解决方法。猜测是iOS系统在绘制键盘的时候会将这个"ABC"按钮覆盖了。然而你并不能告诉产品经理说,bug无法解决。她会告诉你是bug就肯定能找到解决方法的。你只能绞尽脑汁寻找解决办法的。最终在平衡利弊之后采用的解决方案是用其他方式来替换当前"黏贴"方法。这里就需要提及UITextField的两个属性成员变量:inputView 和 inputAccessoryView,怎么理解这两个成员变量呢?
从上面那张图,我们可以将键盘看成有两部分组成(当然,键盘还会有其他的部分组成),一个下面键盘部署,一个内容框。相对应的属性就是上半部分是inputView,下半部分是inputAccessoryView。
我们调用的系统默认键盘的时候,我们看到的键盘view部分就是这个inputView + inputAccessoryView组成。所以如果设置 textField.inputView = nil ,点击textField是不会弹出键盘的,因为这个时候inputView = nil。所以可以通过设置inputView和inputAccessoryView来自定义我们所需要显示的视图。
自定义键盘
自定义键盘就是键盘全部由客户端代码生成,工作量巨大,下面是招行APP的例子:
UIView * keyboardView = /**/
textField.inputView =keyboardView;//输入我们自定义的键盘
textField.inputAccessoryView = nil;
系统键盘+自定义
第一种方法能很好的解决问题,但是这个实现成本有点大,选择一个较为轻量的方法:数字键盘还是使用系统的空间,不改变textField.inputView ,通过设置inputAccessoryView在键盘的顶部添加我们想要的视图。
UIView * keyboardView = /**/
textField.inputAccessoryView =keyboardView;
这个inputAccessoryView是和键盘一起出现的,不会再出现用户切换不了输入法的问题。如果用户有安装第三方键盘,这个inputAccessoryView也是会出现在键盘顶部。这个时候要注意隐藏。
有时候会发现设置了inputAccessoryView = nil并没有起作用,因为设置的时机不对,除了在textFieldShouldBeginEditing函数中设置以外,如果要确保为nil的话,可以用方法[textField reloadInputViews] ,该方法会重新绘制一遍键盘,inputAccessoryView 就为空了。