一個(gè)簡單富文本輸入框控件的設(shè)計(jì)歷程(二)

上一節(jié)主要介紹曾經(jīng)走過的彎路和設(shè)計(jì)思路,這一節(jié)我將介紹具體的實(shí)現(xiàn)方案 和 TextKit 缴淋。為什么要介紹 TextKit 呢?為了讓富文本處理的相關(guān)代碼和 UITextView 分離昼蛀,以達(dá)到高內(nèi)聚宴猾、低耦合。如果直接在 UITextView 中定義一個(gè)類型為 NSMutableAttributedString 的參數(shù)叼旋,也可以實(shí)現(xiàn)相關(guān)功能,但是這樣富文本處理的代碼和 textView 邏輯處理的代碼就冗合在一起沦辙,可讀性和可維護(hù)性比較差夫植。


9417D82C-DF2E-4169-A2BA-A3438F3D70A5.png

TextKit

何為 TextKit ,它是由蘋果官方提供提供油讯、功能強(qiáng)大的內(nèi)容管理框架详民,它可以控制 UITextView 內(nèi)容顯示樣式、布局陌兑、行間距等等很多細(xì)節(jié)沈跨。 TextKit 主要包括三個(gè)類,分別為:NSLayoutManager兔综、NSTextContainer饿凛、NSTextStorage。

  • NSLayoutManager 控制布局软驰;
  • NSTextContainer 控制顯示的區(qū)域和不可以顯示的區(qū)域涧窒;
  • NSTextStorage 內(nèi)容管理器。

蘋果在 GitHub 上提供了 TextKit 的簡單使用和功能演示锭亏。想更進(jìn)一步了解請查閱蘋果官方文檔纠吴,鏈接我貼在下面。
A little demo application showing off some features of the new TextKit classes in iOS 7

TextKit
TextKit is a full-featured, high-level set of classes for handling text and fine typography. Using TextKit, you can lay out styled text into paragraphs, columns, and pages; you can flow text around arbitrary regions such as graphics; and you can use it to manage multiple fonts. If you were considering using Core Text to implement text rendering, you should consider TextKit instead. TextKit is integrated with all UIKit text-based controls to enable apps to create, edit, display, and store text more easily—and with less code than was previously possible in iOS.

TextKit comprises new UIKit classes, along with extensions to existing classes, including the following:

The NSAttributedString class has been extended to support new attributes.
The NSLayoutManager class generates glyphs and lays out text.
The NSTextContainer class defines a region where text is laid out.
The NSTextStorage class defines the fundamental interface for managing text-based content.
For more information about TextKit, see Text Programming Guide for iOS.

NSTextStorage

TextKit 三劍客中慧瘤,使用 NSTextStorage 便可實(shí)現(xiàn)功能戴已。首先將 RichTextStorage (繼承自 NSTextStorage)和 UITextView 綁定,讓其將內(nèi)容管理交于我們定義的內(nèi)容管理器管理锅减。

- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
{
    if (textContainer == nil) {
        textContainer = [[NSTextContainer alloc] init];
    }
    
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [layoutManager addTextContainer:textContainer];
    
    _richTextStorage = [[RichTextStorage alloc] init];
    [_richTextStorage addLayoutManager:layoutManager];
    
    self = [super initWithFrame:frame textContainer:textContainer];
    if (self) {
    }
    
    return self;
}

RichTextStorage 必須覆寫以下方法糖儡。

 - (NSString *)string;
 - (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;

 - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
 - (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range;
  • 第一個(gè)方法是返回當(dāng)前內(nèi)容管理器中管理的文本,換句話說就是顯示到 textView 上的文本上煤;
  • 第二個(gè)方法返回指定位置的字符的屬性信息休玩;
  • 第三個(gè)方法使用字符串替換指定范圍內(nèi)的文本;
  • 第四個(gè)方法設(shè)置內(nèi)容管理器中文本的屬性信息。

處理富文本的代碼在第三個(gè)方法中進(jìn)行拴疤,其他三個(gè)方法按照官方范例覆寫便可永部。官方給出的案例定義了一個(gè)變量 _imp,主要控制顯示到 textView 上的內(nèi)容呐矾,這里我們需要再定義一個(gè)變量 _richImp 用于存放富文本的相關(guān)信息苔埋。_imp 和 _richImp 的關(guān)系很特殊,他們的 string 的值是相同的蜒犯,字符的改變也是同步组橄。不同點(diǎn)在于,_imp 中的屬性只與顯示出來的內(nèi)容相關(guān)罚随,_richImp 中存與富文本信息相關(guān)的內(nèi)容玉工。

富文本處理

在方法 replaceCharactersInRange:withString: 中傳入文本
“你好<a title=‘title’ id=‘001’ type=‘user’ >”
處理后的文本為
你好title @{@"title":@"title", @"id":@"001", @"type" :@"user" };
我們將處理后帶格式的文本更新到 _richImp,不帶格式的更新到 _imp。

這個(gè)時(shí)候你可能會(huì)問淘菩,顯示到 textView 的富文本需要顯示不同的顏色遵班,如何實(shí)現(xiàn)呢?

富文本顯示樣式的控制

在方法 processEditing 中控制富文本的字體和顏色潮改。
我們用 _richImp 調(diào)用 enumerateAttributesInRange: options:usingBlock: 方法狭郑,獲取到的 attrs 便是在上面設(shè)置的字典@[title=title;id=001;type=user] ,range 是該字典的范圍汇在。從中取出 type 的值翰萨,根據(jù) type 的值指定更新 _imp 中的字體和顏色屬性。比如 user 設(shè)置為紅色 15 號字體糕殉,標(biāo)簽設(shè)置為 藍(lán)色 12號字體亩鬼。

- (void)processEditing
{
    NSRange paragaphRange = [self.string paragraphRangeForRange: self.editedRange];
    
    // 更新顏色(枚舉 _richText)
    NSMutableAttributedString *newRichText = [[NSMutableAttributedString alloc] initWithAttributedString:_richImp];
    [newRichText enumerateAttributesInRange:paragaphRange options:NSAttributedStringEnumerationReverse usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
        NSString *type = [attrs valueForKey:@"type"];
        
        if ([type.lowercaseString isEqualToString:@"user"]) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            [dict setValue:color forKey:NSForegroundColorAttributeName];
            [dict setValue:_font forKey:NSFontAttributeName];
    
            [self addAttributes:dict range:range];
        }
    }];
    
    [super processEditing]; // 最后調(diào)用
}

到這里我們基本上完成了富文本的輸入和顯示,但是還存在很多問題還沒有解決糙麦,我這里列舉一下:

  • 光標(biāo)的位置辛孵,現(xiàn)在如果你移動(dòng)光標(biāo),光標(biāo)是可以移動(dòng)到富文本中的赡磅。當(dāng)然如果你有這種需求魄缚,我們就另當(dāng)別論了。

  • 選擇的文本只包含了富文本的部分內(nèi)容焚廊,前半部分冶匹,或者后半部分。

    今天先說到這兒咆瘟,如果你有興趣嚼隘,希望親自實(shí)現(xiàn)一下。我將在下一節(jié)中袒餐,解決這些問題飞蛹,并做一些優(yōu)化谤狡。歡迎各位掃碼加入圈子 【猿圈】,在這里我們一起聊技術(shù)卧檐、聊 bug墓懂、聊那些年我們踩過的坑。

ECD23296A3C9AC33E9F6320A98A070E1.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霉囚,一起剝皮案震驚了整個(gè)濱河市捕仔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盈罐,老刑警劉巖榜跌,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盅粪,居然都是意外死亡钓葫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門票顾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓤逼,“玉大人,你說我怎么就攤上這事库物。” “怎么了贷帮?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵戚揭,是天一觀的道長。 經(jīng)常有香客問我撵枢,道長民晒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任锄禽,我火速辦了婚禮潜必,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沃但。我一直安慰自己磁滚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布宵晚。 她就那樣靜靜地躺著垂攘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淤刃。 梳的紋絲不亂的頭發(fā)上晒他,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機(jī)與錄音逸贾,去河邊找鬼陨仅。 笑死津滞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灼伤。 我是一名探鬼主播触徐,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼饺蔑!你這毒婦竟也來了锌介?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤猾警,失蹤者是張志新(化名)和其女友劉穎孔祸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體发皿,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崔慧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了穴墅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惶室。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖玄货,靈堂內(nèi)的尸體忽然破棺而出皇钞,到底是詐尸還是另有隱情,我是刑警寧澤松捉,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布夹界,位于F島的核電站,受9級特大地震影響隘世,放射性物質(zhì)發(fā)生泄漏可柿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一丙者、第九天 我趴在偏房一處隱蔽的房頂上張望复斥。 院中可真熱鬧,春花似錦械媒、人聲如沸目锭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侣集。三九已至,卻和暖如春兰绣,著一層夾襖步出監(jiān)牢的瞬間世分,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工缀辩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臭埋,地道東北人踪央。 一個(gè)月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像瓢阴,于是被迫代替她去往敵國和親畅蹂。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容