SDL中文论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 4260|回复: 1
打印 上一主题 下一主题

[iOS/Mac] iOS软键盘

[复制链接]

187

主题

346

帖子

2450

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2450
跳转到指定楼层
楼主
发表于 2016-5-14 11:08:16 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ancientcc 于 2016-11-12 09:32 编辑

在iOS/Android,要输入字符必须弹出软键盘。软键盘有几个固定特点,像位在底部、会覆盖整行,同时有个特点是可能出现不同高度,像iOS时从手写输入切换为输入英文,就会降低高度。

个人认为的SDL 2.0.3建议如何处理键盘
1、弹出键盘时会自动把应用程序窗口向上移;
2、结果会发现弹得不够高(底下好多还是被键盘挡住),我认为是SDL获得键盘高度的函数不对,它用了UIKeyboardFrameBeginUserInfoKey,而应该用的是UIKeyboardFrameEndUserInfoKey。
3、根据以上两点应用程要做的:把输入框放在底部。这样弹出键盘时会看到输入框。

SDL 2.0.3处理缺陷
  • 应用程序不能获得键盘高度,这样不能精确到像素来计算到底要显示多高。
  • 应用程序知道何时弹出、隐藏键盘(弹出一般是它主动触发,隐藏或是可主动触发或是收到“回车”键),但也该知道软盘何时高度发生了变化,这样可以根据高度实时调整窗口。

修改 SDL 2.0.3
SDL库应该要做到两点
1、键盘高度发生变化时,能收到通知(包括从没到有)
修改center addObserverForName:UIKeyboardWillShowNotification,在调用_uikit_keyboard_set_height(height)后增加以下代码
  1. CGSize keyboardEndSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
  2. height = keyboardEndSize.width;
  3. keyboardHeight = height;
  4. SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_PRINTSCREEN);
复制代码

1)iOS存在个键盘显示动画,UIKeyboardFrameBeginUserInfoKey、UIKeyboardFrameEndUserInfoKey分别对应动画开始、结束时刻的键盘高度,这里以结束时刻高度为键盘高度。
2)应用程序固定使用场景模式,高、宽要互换(准确的话应该根据朝向判断要不要执行互换,如何判断见该函数原有代码)。
3)把这个度度放在全局变量keyboardHeight。
4)向上发一个扫描码是SDL_SCANCODE_PRINTSCREEN键盘按下事件,模拟键盘高度通知。用SDL_SCANCODE_PRINTSCREEN作为扫描码,因为我这个应用程序不用它。

2、得到键盘高度
我这个应用程序不用原SDL_SetTextInputRect提供的功能,避免再加API,复用它得到这个高度。
  1. void UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
  2. {
  3.         ......
  4.         rect->h = keyboardHeight;
  5.         return;
  6.         view.textInputRect = *rect;
  7. }
复制代码


有了以上两个修改,应用可按以下进行编程。代码不断侦听SDL事件,如果收到键盘码是SDL_SCANCODE_PRINTSCREEN的SDL_KEYDOWN,那意味着键盘高度有变化了(包括从无到有),接下就可调用SDL_SetTextInputRect来得到高度。
  1. if (key == SDLK_PRINTSCREEN) {
  2.         SDL_Rect rc;
  3.         SDL_SetTextInputRect(&rc);
  4.         settings::keyboard_height = rc.h;
  5. }
复制代码



以上修改只是临时顶上,希望SDL下个版本会增强软键盘相关功能。
回复

使用道具 举报

187

主题

346

帖子

2450

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2450
沙发
 楼主| 发表于 2016-5-14 11:09:02 | 只看该作者
本帖最后由 ancientcc 于 2016-11-12 10:42 编辑

为以下叙述明了,贴张九宫格输入法。

我把它归为需要联想的输入法,即系统会根据输入的字根联想出一些字/词。就拿上图来说,“cai”是输入的三个字根。按这个逻辑,全键盘拼音也是联想输入法。

SDL2.0.4不支持拼音、九宫格(10 key)输入
为什么不支持?根源出在shouldChangeCharactersInRange,该函数返回值都是NO,使得textField得到一个字符就扔掉,没了联想输入法基于的字根。因为没有字根,在输入法界面就不会出现第一行的字根组合、第二行的汉字。

  1. (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
复制代码

功能:
询问重载者,是否把textField中位置为range的字符串替换为string字符串。返回YES时要替换,对于联想输入法来说,就是把当前输入的string做为字根加入缓存。返回NO时不替换,这意味着string不会被加入textField,它被扔掉。因而像九宫格、中文全拼,它们要能起作用的话必须返回YES。

此函数在textField内容被修改时就立刻被调用,所以能动态记录用户的输入。但是,它有个缺陷,就是当用户输入汉字的时候,此方法中的参数string只是字母,而不是中文!举个例子,当你输入cai(蔡)的时候,它记录的分别是cai,所以对于中文的话,也就无法动态获取了。但是,当用手写汉字输入时,它能得到最终汉字,但得不到中间过程(中间过程指是在手写过程中,系统会联想出一些字/词,此函数收不到这些字/词)。

解决的办法就是给textfield加个状态监听器。这点以下会说,先深入下textField的字符缓存。

在软键盘输入时,textField内部有个缓存,它缓存着输入字符,包括“backsapce”。以下是这个缓存变化的一个例子。假设该缓存已有字符“ancient”
  • 用户输入“c”——ancientc
  • 用户输入“a”——ancientca
  • 用户输入“i”——ancientcai
  • 用户在汉字栏选中“蔡”——ancient蔡(注:shouldChangeCharactersInRange检测不到这个输入,上面有说)

在第4步,iOS自动把“cai”变成了“蔡”。
假设在第4步用户输入“backsapce”,那缓存中是“ancientca”,软键盘第一行相应变成“ca”。

到此大概可以看到iOS内置编辑框的工作机制,它就是把textField缓存呈现给用户。

现在考虑非iOS内置编辑框如何正确读出软键盘,比如SDL。上面说到shouldChangeCharactersInRange会出现无法检测汉字时有说到“状态监听器”,这个监听器是两个函数。

  1. - (void)viewDidLoad
  2. {
  3.         [_textField addTarget:self action:@selector(textFieldDidChanged:) forControlEvents:UIControlEventEditingChanged];

  4.         [[NSNotificationCenter defaultCenter] addObserver:self
  5.                                                  selector:@selector(changeValue:)
  6.                                                      name:@"changeValue" object:nil];

  7. }

  8. - (void)textFieldDidChanged:(UITextField *)textField
  9. {
  10.         NSLog(@"textField text : %@", [textField text]);
  11. }

  12. -(void)changeValue:(NSNotification *)notification
  13. {
  14.         UITextField *textField = notification.object;
  15.         NSLog(@"textField text : %@", [textField text]);
  16. }
复制代码
两个状态监听器行为是一样的,我修改时是使用UIControlEventEditingChanged。

会遇到即使只是输入一个字符,textFieldDidChanged会被调用多次。只要深入字符缓存会发现,这个多次是由于缓存的确变换了多次!举个例子,用户按下九宫格中的“abc”块,iOS认为该是“b”,textFieldDidChanged就被调用一次,很快又发现“a”更好,于是textFieldDidChanged又被调用一次。

在textFieldDidChanged,[textField text]得到的就是缓存,代码可根据缓存的变换计算出基于SDL的app中编辑框该是什么样子。以下简单说下逻辑,具体代码参考SDL_uikitviewcontroller.m中的textFieldDidChanged函数。
  • 记住最近一次缓存内容,存放在类型是NSString的last_text_field_text变量。
  • textFieldDidChanged被调用,表示有新字符到来(也有可能是backspace导致),比较此次和上次缓存中内容,找到不同的第一个字节位置,记为first_byte。
  • 缓存中内容可能含有多字节字符,first_byte可能恰好位在多字节中间,为此基于first_byte找出不同的第一个字符位置,记为first_char。
  • 根据fist_char和last_text_field_text进行计算,一旦算出有backspace,向上发送等数量SDL_SCANCODE_BACKSPACE。
  • 根据first_char和此次缓存内容,用SDL_TEXTINPUT发送“新”到的内容。


当光标在编辑框中位置发生改变时发生了什么?

为什么textField不能是空
一旦textField是空,在软键盘按下backspace就不会触发shouldChangeCharactersInRange,结果就是SDL不可能收到这个backsapce(应用程序的编辑框中字符串和textField缓存中内容是有关联,但不是一回事)。为不让空,代码会保证textField中的text至少会有一个“空格”符。

再说下修改后的shouldChangeCharactersInRange,它基本就是什么都不干,直接返回YES,除了收到“backsapce”。收到“backsapce”,而且缓存中只剩一个字符时,它直接向app发送SDL_SCANCODE_BACKSPACE,然后返回NO。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|丽谷软件|libsdl.cn

GMT+8, 2024-5-20 02:07 , Processed in 0.055164 second(s), 22 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表