iOS使用UITableView實(shí)現(xiàn)的富文本編輯器

前言

公司最近做一個(gè)項(xiàng)目固翰,其中有一個(gè)模塊是富文本編輯模塊收厨,之前沒做個(gè)類似的功能模塊,本來以為這個(gè)功能很常見應(yīng)該會(huì)有已經(jīng)造好的輪子砸讳,或許我只要找到輪子琢融,研究下輪子,然后修改打磨輪子簿寂,這件事就八九不離十了漾抬。不過,還是?too young to simple了常遂,有些事纳令,還是得自己去面對(duì)的,或許這就叫做成長,感覺最近一年平绩,對(duì)于編程這件事圈匆,更多了一點(diǎn)熱愛窘拯,我感覺我不配過只會(huì)復(fù)制粘貼代碼的人生柳沙,編程需要有挑戰(zhàn)。所以屯掖,遇到困難性湿,保持一份正念纬傲,路其實(shí)就在腳下,如果沒有困難肤频,那就制造困哪叹括,迎難而上,人生沒有白走的路宵荒,每一步都算數(shù)汁雷,毒雞湯就到此為止,下面是干貨了报咳。

結(jié)果

實(shí)現(xiàn)的功能包含了:

編輯器文字編輯

編輯器圖片編輯

編輯器圖文混排編輯

編輯器圖片上傳摔竿,帶有進(jìn)度和失敗提示,可以重新上傳操作

編輯器模型轉(zhuǎn)換為HTML格式內(nèi)容

簡(jiǎn)單的本地?cái)?shù)據(jù)存儲(chǔ)和恢復(fù)編輯實(shí)現(xiàn)(草稿箱功能)

配套的Java實(shí)現(xiàn)的服務(wù)器

后期有進(jìn)行了性能的優(yōu)化少孝,可以看我的這篇文章:?iOS使用Instrument-Time Profiler工具分析和優(yōu)化性能問題

以及客戶端代碼開源托管地址:MMRichTextEdit

還有java實(shí)現(xiàn)的文件服務(wù)器代碼開源托管地址:javawebserverdemo

沒圖沒真相,下面是幾張實(shí)現(xiàn)的效果圖




調(diào)研分析

基本上有以下幾種的實(shí)現(xiàn)方案:

UITextView結(jié)合NSAttributeString實(shí)現(xiàn)圖文混排編輯熬苍,這個(gè)方案可以在網(wǎng)上找到對(duì)應(yīng)的開源代碼稍走,比如?SimpleWord?的實(shí)現(xiàn)就是使用這種方式,不過缺點(diǎn)是圖片不能有交互柴底,比如說在圖片上添加進(jìn)度條婿脸,添加上傳失敗提示,圖片點(diǎn)擊事件處理等等都不行柄驻,如果沒有這種需求那么可以選擇這種方案狐树。

使用WebView通過js和原生的交互實(shí)現(xiàn),比如?WordPress-Editor鸿脓、RichTextDemo?抑钟,主要的問題就是性能不夠好,還有需要你懂得前端知識(shí)才能上手野哭。

使用CoreText或者TextKit在塔,這種也有實(shí)現(xiàn)方案的開源代碼,比如說這個(gè)?YYText拨黔,這個(gè)很有名氣蛔溃,不過他使用的圖片插入編輯圖片的位置是固定的,文字是圍繞著圖片,所以這種不符合我的要求贺待,如果要使用這種方案徽曲,那修改的地方有很多,并且CoreText/TextKit使用是有一定的門檻的麸塞。

使用UITableView結(jié)合UITextView的假實(shí)現(xiàn)秃臣,主要的思路是每個(gè)Cell是一個(gè)文字輸入的UITextView或者是用于顯示圖片使用的UITextView,圖片顯示之所以是選擇UITextView是因?yàn)閳D片位置需要有輸入光標(biāo)喘垂,所以使用UITextView結(jié)合NSAttributeString的方式正好可以實(shí)現(xiàn)這個(gè)功能甜刻。圖片和文字混排也就是顯示圖片的Cell和顯示文字的Cell混排就可以實(shí)現(xiàn)了,主要的工作量是處理光標(biāo)位置輸入以及處理光標(biāo)位置刪除正勒。

選型定型

前面三種方案都有了開源的實(shí)現(xiàn)得院,不過都不滿足需要,只有第二種方案會(huì)比較接近一點(diǎn)章贞,不過WebView結(jié)合JS的操作確實(shí)是性能不夠好祥绞,內(nèi)存占用也比較高,?WordPress-Editor?鸭限、RichTextDemo?蜕径,這兩種方法實(shí)現(xiàn)的編輯器會(huì)明顯的感覺到不夠流暢,并且離需要還有挺大的距離败京,所有沒有選擇在這基礎(chǔ)上進(jìn)行二次開發(fā)兜喻。第三種方案在網(wǎng)上有比較多的人推薦,不過我想他們大概也只是推薦而已赡麦,真正實(shí)現(xiàn)起來需要花費(fèi)大把的時(shí)間朴皆,需要填的坑有很多,考慮到時(shí)間有限遂铡,以及項(xiàng)目的進(jìn)度安排们衙,這個(gè)坑我就沒有去踩了蒂教。

我最終選擇的是第四種方案懊悯,這種方案好的地方就是UITableView剑肯、UITextView都是十分熟悉的組件让网,使用組合的模式通過以上的分析溃睹,理論上是沒有問題的泞辐,并且,UITableView有復(fù)用Cell的優(yōu)勢(shì)锯茄,所以時(shí)間性能和空間性能應(yīng)該是不差的礁叔。

實(shí)現(xiàn)細(xì)節(jié)分析

使用UITableView集合UITextView的這種方案有很多細(xì)節(jié)需要注意

Cell中添加UITextView讥蔽,文字輸入換行或者超過一行Cell高度自動(dòng)伸縮處理

Cell中添加UITextView顯示圖片的處理

光標(biāo)處刪除和添加圖片的處理,換行的處理

需要解決問題响禽,好的是有些是已經(jīng)有人遇到并且解決的荚醒,其他的即使其他人沒有遇到過隆嗅,作為第一個(gè)吃螃蟹的人,我們?cè)敿?xì)的去分析下其實(shí)也不難

這個(gè)問題剛好有人遇到過胖喳,這里就直接發(fā)鏈接了iOS UITextView 輸入內(nèi)容實(shí)時(shí)更新cell的高度

實(shí)現(xiàn)上面效果的基本原理是:

1.在 cell 中設(shè)置好 text view 的 autolayout泡躯,讓 cell 可以根據(jù)內(nèi)容自適應(yīng)大小

2.text view 中輸入內(nèi)容较剃,根據(jù)內(nèi)容更新 textView 的高度

3.調(diào)用 tableView 的 beginUpdates 和 endUpdates确垫,重新計(jì)算 cell 的高度

4.將 text view 更新后的數(shù)據(jù)保存帽芽,以免 table view 滾動(dòng)超過一屏再滾回來 text view 中的數(shù)據(jù)又不刷新成原來的數(shù)據(jù)了导街。

注意:上面文章中提到的思路是對(duì)的披泪,不過在開發(fā)過程中遇到一個(gè)問題:使用自動(dòng)布局計(jì)算高度的方式調(diào)用 tableView 的 beginUpdates 和 endUpdates,重新計(jì)算 cell 的高度會(huì)出現(xiàn)一個(gè)嚴(yán)重的BUG搬瑰,textView中的文字會(huì)偏移導(dǎo)致不在正確的位置款票,所以實(shí)際的項(xiàng)目中禁用了tableView自動(dòng)計(jì)算Cell高度的特性,采用手動(dòng)計(jì)算Cell高度的方式泽论,具體的可以看我的項(xiàng)目代碼艾少。

2.這個(gè)問題很簡(jiǎn)單,使用屬性文字就行了翼悴,下面直接貼代碼了

NSAttributedString結(jié)合NSTextAttachment就行了

3.這個(gè)問題比較棘手缚够,我自己也是先把可能的情況列出來,然后一個(gè)一個(gè)分支去處理這些情況鹦赎,不難就是麻煩谍椅,下面的文本是我寫在?備忘錄?上的情況分析,- [x] 這種標(biāo)識(shí)這種情況已經(jīng)實(shí)現(xiàn)古话,- [ ] 這種標(biāo)識(shí)暫時(shí)未實(shí)現(xiàn)雏吭,后面這部分會(huì)進(jìn)行優(yōu)化,主要的工作已經(jīng)完成了陪踩,優(yōu)化的工作量不會(huì)很大了杖们。

基本上分析就到此為止了悉抵,talk is cheap, show me code,下面就是代碼實(shí)現(xiàn)了胀莹。

代碼實(shí)現(xiàn)

編輯模塊

文字輸入框的Cell實(shí)現(xiàn)

下面是文字輸入框的Cell的主要代碼基跑,包含了

初始設(shè)置文字編輯Cell的高度、文字內(nèi)容描焰、是否顯示Placeholder

在?UITextViewDelegate?回調(diào)方法?textViewDidChange?中處理Cell的高度自動(dòng)拉伸

刪除的回調(diào)方法中處理前面刪除和后面刪除媳否,刪除回調(diào)的代理方法是繼承?UITextView?重寫?deleteBackward?方法進(jìn)行的回調(diào),具體的可以額查看?MMTextView?這個(gè)類的實(shí)現(xiàn)荆秦,很簡(jiǎn)單的一個(gè)實(shí)現(xiàn)篱竭。

顯示圖片Cell的實(shí)現(xiàn)

下面顯示圖片Cell的實(shí)現(xiàn),主要包含了

初始設(shè)置文字編輯Cell的高度步绸、圖片顯示內(nèi)容

在?UITextViewDelegate?回調(diào)方法?shouldChangeTextInRange?中處理換行和刪除掺逼,這個(gè)地方的刪除和Text編輯的Cell不一樣,所以在這邊做了特殊的處理瓤介,具體看一看?shouldChangeTextInRange?這個(gè)方法的處理方式吕喘。

處理圖片上傳的進(jìn)度回調(diào)、失敗回調(diào)刑桑、成功回調(diào)

圖片上傳模塊

圖片上傳模塊中氯质,上傳的元素和上傳回調(diào)抽象了對(duì)應(yīng)的協(xié)議,圖片上傳模塊是一個(gè)單利的管理類祠斧,管理進(jìn)行中的上傳元素和排隊(duì)中的上傳元素闻察,

圖片上傳的元素和上傳回調(diào)的抽象協(xié)議

圖片上傳的管理類

圖片上傳使用的是?NSURLSessionUploadTask?類處理

在?completionHandler?回調(diào)中處理結(jié)果

在NSURLSessionDelegate?的方法?URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:?中處理上傳進(jìn)度

在NSURLSessionDelegate?的方法?URLSession:task:didCompleteWithError:?中處理失敗

上傳管理類的關(guān)鍵代碼如下:

圖片上傳的回調(diào)會(huì)通過?UploadItemCallBackProtocal?協(xié)議的實(shí)現(xiàn)方法回調(diào)到圖片編輯的模型中,更新對(duì)應(yīng)的數(shù)據(jù)琢锋。圖片編輯的數(shù)據(jù)模型是?MMRichImageModel?辕漂,該模型實(shí)現(xiàn)了?UploadItemProtocal?和?UploadItemCallBackProtocal?協(xié)議,實(shí)現(xiàn)?UploadItemCallBackProtocal?的方法更新數(shù)據(jù)模型的同時(shí)吴超,會(huì)通過delegate通知到Cell更新進(jìn)度和失敗成功的狀態(tài)钉嘹。 關(guān)鍵的實(shí)現(xiàn)如下

內(nèi)容處理模塊

最終是要把內(nèi)容序列化然后上傳到服務(wù)端的,我們的序列化方案是轉(zhuǎn)換為HTML鲸阻,內(nèi)容處理模塊主要包含了以下幾點(diǎn):

生成HTML格式的內(nèi)容

驗(yàn)證內(nèi)容是否有效隧期,判斷圖片時(shí)候全部上傳成功

壓縮圖片

保存圖片到本地

這部分收尾的工作比較的簡(jiǎn)單,下面是實(shí)現(xiàn)代碼:

總結(jié)

這個(gè)功能從選型定型到實(shí)現(xiàn)大概花費(fèi)了3天的時(shí)間赘娄,因?yàn)闀r(shí)間原因,有很多地方優(yōu)化的不到位宏蛉,如果看官有建議意見希望給我留言遣臼,我會(huì)繼續(xù)完善,或者你有時(shí)間歡迎加入這個(gè)項(xiàng)目拾并,可以一起做得更好揍堰,代碼開源看下面的鏈接鹏浅。

代碼托管位置

客戶端代碼開源托管地址:MMRichTextEdit

java實(shí)現(xiàn)的文件服務(wù)器代碼開源托管地址:javawebserverdemo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屏歹,隨后出現(xiàn)的幾起案子隐砸,更是在濱河造成了極大的恐慌,老刑警劉巖蝙眶,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件季希,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡幽纷,警方通過查閱死者的電腦和手機(jī)式塌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來友浸,“玉大人峰尝,你說我怎么就攤上這事∈栈郑” “怎么了武学?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伦意。 經(jīng)常有香客問我火窒,道長,這世上最難降的妖魔是什么默赂? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任沛鸵,我火速辦了婚禮,結(jié)果婚禮上缆八,老公的妹妹穿的比我還像新娘曲掰。我一直安慰自己,他們只是感情好奈辰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布栏妖。 她就那樣靜靜地躺著,像睡著了一般奖恰。 火紅的嫁衣襯著肌膚如雪吊趾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天瑟啃,我揣著相機(jī)與錄音论泛,去河邊找鬼。 笑死蛹屿,一個(gè)胖子當(dāng)著我的面吹牛屁奏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播错负,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坟瓢,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼勇边!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起折联,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤粒褒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后诚镰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奕坟,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年怕享,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了执赡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡函筋,死狀恐怖沙合,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情跌帐,我是刑警寧澤首懈,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站谨敛,受9級(jí)特大地震影響究履,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脸狸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一最仑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炊甲,春花似錦泥彤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颈娜,卻和暖如春剑逃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背官辽。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工蛹磺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人同仆。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓萤捆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鳖轰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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

  • 最好的我們熱播蕴侣,喚起了眾票青春回憶,曾經(jīng)的那個(gè)他臭觉,或者曾經(jīng)沒能成為那個(gè)他的他昆雀,現(xiàn)在怎么樣? 也許都沒有蝠筑,那么曾經(jīng)的...
    小二ivy閱讀 466評(píng)論 0 1
  • 孩子書包落在樓下車筐里,沒有拿上樓 臣镣。他發(fā)脾氣辅愿,不寫作業(yè),把我在樓上給他找的紙筆橡皮都甩在地上忆某。 我耐住...
    白羽1413閱讀 477評(píng)論 1 1
  • 今年春節(jié)計(jì)劃去韓國旅游点待,放松一下身心,同時(shí)增長一下見識(shí)弃舒。本來想就當(dāng)休閑游癞埠,也不做設(shè)么攻略了,但是想想聋呢,至少基本的交...
    浮云狒閱讀 480評(píng)論 0 0
  • 其實(shí)我們都不想做一個(gè)安于現(xiàn)狀的人苗踪,所以學(xué)會(huì)升級(jí)自己的生命尺度是很關(guān)鍵的,不斷的去提升自己坝冕,不斷的去努力徒探,不斷的讓...
    合肥李風(fēng)麗閱讀 168評(píng)論 0 0
  • 質(zhì)樸食材意真誠,兒食母做好心情喂窟。 一年共進(jìn)家常飯测暗,四海奔波思更濃。
    思索者閱讀 149評(píng)論 2 2