第二篇 编辑器控件

功能描述

        这个控件能够以Markdown文件格式约定,把文件呈现给用户,并提供交互操作。

        这个最核心的是接受用户输入,在用户点击时能够调起输入法,并和输入法有互动。实际上,这篇文档主要描述UITextInput协议的相关细节。


UITextInput协议概述

        这个协议就是和系统输入法的交互协议,系统控件UITextField和UITextView等都实现了这个协议。

        实现该协议的控件,应该在内部维护一个或一组String,且一般来说,应该是UIView的一个子类,我们需要实现绘制,按我们目的渲染String。


UITextPosition和UITextRange

        这两个类分别标记了位置和区间,默认的区间就是两个位置组成的(看UITextRange)的定义就能发现(过度的抽象会导致扩展的难度,这里的抽象,不是指对现实增加一个抽象层,而是指对现实增加一个条件假设的抽象)。因为在UITextInput协议的方法直接,需要传输位置和区间概念。例如,输入法(或者说是系统)需要获取指定区间的String,这时就会给我们一个UITextRange的子类实例。

        这个协议里所有这两个实例的传入参数,都是这个协议里,其他方法返回给系统的。借此说明几个方法(函数或者怎么翻译?)的意义:

        1、func textRange(fromfromPosition:UITextPosition, totoPosition:UITextPosition) ->UITextRange?
            这个方法就是有两个位置生成一个区间的方法。fromfromPosition和totoPosition分别是区间的起止位置。

        2、func compare(_ position:UITextPosition, toother:UITextPosition) ->ComparisonResult

            这个方法用来比较两个位置的相对关系。这里计算的是一维维度下的位置关系

            需要注意的是两个UITextPostion实例的位置关系有两个维度,分别为一维维度和二维维度。

            1)、一维维度

                在一维维度没有行的概念,认为一行的行尾在下一行的行首前,并且紧邻(这里我没有研究过阿拉伯语等从右向左书写的语言的实际情况)

            2)、二维维度

                二维维度,就是平时的文本文档的位置关系,及两个位置之间有水平关系和垂直关系两种。一般情况下,垂直关系上使用间隔的行数(通常为响应键盘的方向键上或下一次进行调整的单位量),水平关系上使用间隔的字符数(通常为响应键盘的方向键左或右,这里需要注意ejimo表情等字符串,我们要当成一个字符间隔,否则很难想象光标移动到表情中间的样子……)

        3、func position(fromposition:UITextPosition, offset:Int) ->UITextPosition?

            计算一个位置在特定偏移后的一维维度的新位置。需要注意的一个细节是offset的正负表示了移动的方向。

        4、func position(fromposition:UITextPosition, indirection:UITextLayoutDirection, offset:Int) ->UITextPosition?

            计算一个位置在特定偏移后的二维维度的新位置。需要注意offset为负时,对indirection的影响。

            这个方法的实现代码上,我在水平方向上从用了上一个方法的实现。当然,也可以反过来,实现这个方法,然后上一个方法调用这个方法的实现。我没有选择这个方法的原因就是这样导致这个方法太重。

            从实现层面上,选择那种方案主要取决于一个问题?当光标在行首时,再次按左键,光标去上一行行首?本行行首?还是哪里?我选择去上一行行首,所以逻辑层面,水平方向上就是一维维度关系。

        和位置、区间相关的方法其他就不详细解释啦。

        PS:我在实现上强制了书写方向为从左到右,这可能导致我对部分的理解不全面。


selectedTextRange属性

        前面说过,该协议的实现类需要维护一个或一组String,我们要在用户面前呈现出来,并且响应用户交互。这个响应,一个重要的功能就是用户选中,而selectedTextRange表示的就是用户选中的区域。

        PS:系统通过func text(inrange:UITextRange) ->String?活动特定区域的文本,并不是只有选中。

        对于这个属性,需要特别注意一点,这是一个读写属性,系统是能修改的(这个协议所有返回UITextRange实例的方法的返回值,都有可能是这个属性被修改为的目标值)

        在现实上,selectedTextRange标识的String要和其他字符串有视觉上的差别,否则用户找不到选中呀。


markedTextRange属性

        系统输入法(中文)输入使用,这个一定要实现呀。

        这个属性的含义是这个区间的文本是输入法在输入过程中的添加或修改的。注意:这是一个只读属性,系统不会之间修改这个属性,但系统确实需要修改这个属性,故通过下面两个方法完成:

        1、func setMarkedText(_ markedText:String?, selectedRange:NSRange)

            这个方法表示修改markedTextRange属性的文本为markedText。

            我的实现逻辑为(只根据条件执行一条)

            1)、如果markedTextRange属性有效、则把markedTextRange区间的内容修改为markedText,并把markedTextRange的值同步给selectedTextRange属性

            2)、如果selectedTextRange属性有效,则把selectedTextRange区间的内容修改为markedText,并把selectedTextRange的值同步给markedTextRange属性

            3)、在光标所在位置添加markedText的内容,并更新markedTextRange的值,然后把markedTextRange的值同步给selectedTextRange属性

            这里我把markedTextRange属性和selectedTextRange属性强绑定啦,这只是为了方便,并不是规范。另外,我这么做也是参考了Apple的例子(https://developer.apple.com/library/archive/samplecode/SimpleTextInput/Introduction/Intro.html)。

            selectedRange参数,我实际调试发现:location的值一直为markedText的长度、length的值一直为0。故我忽略了这个参数。

        2、func unmarkText()

            这个方法表示置空markedTextRange属性。这样实现markedTextRange属性标识的文本变成真实内容,表示一个输入动作的完成。

            因为我把markedTextRange属性和selectedTextRange属性强绑定了,所以也一起置空了selectedTextRange属性。

            在我的实现上只有用户有输入焦点,selectedTextRange属性就不会为空,selectedTextRange属性表示的区间收尾一致,表示没有选中,且代表了光标的位置。所以,我刚刚表述的置空了selectedTextRange属性的实际行为是把selectedTextRange属性的起止位置都标记为markedTextRange属性置空前的结束位。


UIKeyInput协议

        UITextInput协议是UIKeyInput协议的子协议,这个协议的两个方法也很有用:

        1、func deleteBackward()

            需要在这个方法里实现输入法中删除的响应。

            当接入蓝牙键盘等设备时,只有键盘的backspace键会触发这个方法。键盘的delete键,没有回调。这个问题我暂时没有处理。

        2、func insertText(_ text:String)

            这个方法表示在光标位置插入text的内容。目前实际测试发现搜狗输入法在使用(而不是markedTextRange属性)


定制deleteBackward

        在点击输入法的删除按钮时,deleteBackward会被调用。

        但框架会帮我们做一些事情,简单来说,如果当前选中如果为空(selectedTextRange属性有效、且isEmpty为true),就会改写selectedTextRange为当前位置前一个位置和当前位置的Range,这时deleteBackward的代码就是简单的删除选中,多数时候比较方便的。

        但如果我们需要扩展删除功能(例如,我需要光标在特定位置时,删除行为不是只删除前一个字符),就需要进行一些改写。

        在deleteBackward被调用前,会依次调用一下接口:

        1、func position(from position:UITextPosition, offset:Int) ->UITextPosition? 根据当前位置计算前一个位置

        2、func textRange(from fromPosition:UITextPosition, to toPosition:UITextPosition) ->UITextRange? 根据两个位置生成一个区域UITextRange

        3、selectedTextRange属性的set,修改选中

        这里,我的扩展方案是1步骤的方法不实现,以达到终端流程的目的。

        注意:func position(fromposition:UITextPosition, indirection:UITextLayoutDirection, offset:Int) ->UITextPosition? 这个方法最好实现,它主要是处理键盘方向键控制光标移动的。


效率问题

        该协议的所有属性和方法都有可以被系统频繁调用,尽量不要做耗时操作。


关联文档

        第一篇 开发构想

        第三篇 导入导出功能

        第四篇 App的内购功能

        第五篇 本地化

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343