iOS 基于MVVM設(shè)計(jì)模式的微信朋友圈開發(fā)

前言
  • 微信朋友圈一直以來(lái)都是iOS開發(fā)人員爭(zhēng)相模仿的界面,主要是其包含了豐富的iOS所需知識(shí)點(diǎn),以及常用的功能模塊。當(dāng)然各個(gè)功能模塊實(shí)現(xiàn)過(guò)程中的細(xì)節(jié)處理以及用戶體驗(yàn)的優(yōu)化往声,這才是我們開發(fā)者在日常開發(fā)中需要關(guān)注和加強(qiáng)的地方。
  • 本文筆者將著重分析微信朋友圈實(shí)現(xiàn)的具體過(guò)程以及細(xì)節(jié)處理操刀,爭(zhēng)取把里面的所有知識(shí)點(diǎn)烁挟,模塊雖小,但五臟俱全骨坑,其中最主要分析的是朋友圈的界面布局的細(xì)節(jié)處理以及性能優(yōu)化撼嗓。希望為大家提供一點(diǎn)思路,少走一些彎路欢唾,填補(bǔ)一些細(xì)坑且警。文章僅供大家參考,若有不妥之處礁遣,還望不吝賜教斑芜,歡迎批評(píng)指正。
  • 微信朋友圈的基本架構(gòu)是基于MVVM + RAC + ViewModel-Based Navigation來(lái)實(shí)現(xiàn)的祟霍,如若不懂杏头,還請(qǐng)點(diǎn)擊iOS 基于MVVM + RAC + ViewModel-Based Navigation的微信開發(fā)(一)
  • 微信朋友圈的界面控件布局和富文本顯示內(nèi)容沸呐,主要是使用YYKit來(lái)完成的醇王,若對(duì)其不熟練的,請(qǐng)事先做好準(zhǔn)備哦崭添。
分析

前期在敲代碼之前寓娩,需要著重分析一下整個(gè)微信朋友圈界面的實(shí)現(xiàn)方案,這可能是本篇文章的核心所在了(PS:這里特別提醒一下廣大開發(fā)者呼渣,在實(shí)現(xiàn)某一個(gè)功能前棘伴,請(qǐng)務(wù)必確定一個(gè)實(shí)現(xiàn)方案,可能實(shí)現(xiàn)的方案千千萬(wàn)屁置,這就需要開發(fā)者通過(guò)自身的理解來(lái)確定一個(gè)最優(yōu)的方案來(lái)實(shí)現(xiàn)焊夸,而不是一昧毫無(wú)頭緒的敲代碼,造成后期又得重新迭代的悲勭掷纭4镜亍2篮K荨)颇象。微信朋友圈的效果圖如下(PS:萬(wàn)惡的馬賽克...)。


Moment.jpeg

當(dāng)然整體的界面的布局還是比較復(fù)雜的并徘,前期看了UI還是挺讓人望而卻步的遣钳。首先,我們可以確定的是整體是利用UITableView來(lái)實(shí)現(xiàn)的麦乞,是不是大家已經(jīng)隱隱約約感受到還是原來(lái)的配方蕴茴,還是熟悉的味道,相同的tableView姐直,變得只是Cell罷了倦淀。其次,筆者經(jīng)過(guò)多日在GitHub上搜尋一些實(shí)現(xiàn)微信朋友圈的開發(fā)的Demo声畏,以及做了大量的市場(chǎng)調(diào)研和內(nèi)容對(duì)比撞叽,發(fā)現(xiàn)最具代表性的兩個(gè)Demo分別是:gsdios/GSD_WeiXinzhengwenming/WeChat,其他Demo大多數(shù)都是參考這兩個(gè)Demo來(lái)做的插龄,當(dāng)然這兩個(gè)Demo實(shí)現(xiàn)微信朋友圈的方法涉及到兩個(gè)不同的方案愿棋,筆者就帶大家簡(jiǎn)單分析一下各自的方案實(shí)現(xiàn)過(guò)程以及目前存在的弊端(PS:這里所謂的弊端,只是針對(duì)微信朋友圈而言的)均牢。兩者的界面模塊劃分如下(PS: ① 紅色框 糠雨, ②:綠色框):

Moment_UI.jpeg

當(dāng)然這兩個(gè)Demo的實(shí)現(xiàn)朋友圈的 共同之處就是:將圖上所示的紅框①整體用一個(gè)UITableViewCell來(lái)展示。不同之處 就是:圖上所示的綠框②的控件選取不同罷了徘跪。
UITableViewCell上布局子控件相對(duì)于大家肯定是小菜一碟甘邀,這里筆者就針對(duì)兩個(gè)Demo綠框②的控件的選取上做文章以及分析其目前存在的弊端。當(dāng)然這兩個(gè)方案目前都不是最最優(yōu)化的方案垮庐,通過(guò)分析其中存在弊端松邪,逐漸引申出比較令人合理的方案,當(dāng)然筆者最終會(huì)給出自己的方案突硝,但也許未必是最優(yōu)的方案测摔,更好的方案或許就存在大家的手中,筆者這里主要強(qiáng)調(diào)的是 知其然解恰,知其所以然锋八。話不多說(shuō)篡撵,Let's Do It琳疏!

  • 方案一 【gsdios/GSD_WeiXin
    該方案將綠框②的控件選取的是一個(gè)普通的UIView吮炕,當(dāng)然內(nèi)部顯示文本(評(píng)論羡宙、回復(fù)冀宴、點(diǎn)贊)的子控件用的是UILabel來(lái)展示政冻。雖然這種寫起來(lái)比較通俗易懂牢硅,就是根據(jù)評(píng)論列表點(diǎn)贊列表的內(nèi)容蒙谓,不斷修改內(nèi)部UILabelframe來(lái)達(dá)到要求,但是卻帶來(lái)了如下的弊端:

    1. 布局復(fù)雜:考慮到綠框②內(nèi)部子控件的布局的復(fù)雜性欺嗤,其作者采用的是其自己寫的SDAutoLayout來(lái)實(shí)現(xiàn)参萄,筆者對(duì)SDAutoLayout用的也不是非常熟練,關(guān)于其布局代碼的實(shí)現(xiàn)請(qǐng)留意其DemoSDTimeLineCellCommentView.h/m文件即可煎饼,盡管其內(nèi)部布局代碼看起來(lái)還算簡(jiǎn)單讹挎,但是如果我們不使用SDAutoLayout,那么采用傳統(tǒng)的frame布局吆玖,想想還是比較復(fù)雜的筒溃,比如:我們要計(jì)算出紅框①(UITableViewCell)的高度,首先需要計(jì)算出綠框②內(nèi)部所有子控件(UILabel)的尺寸沾乘,從而推算出綠框②的整體高度怜奖,最終方能確定紅框①(UITableViewCell)的高度。筆者猜想該作者這里可能主要是為了凸顯SDAutoLayout的自動(dòng)布局的強(qiáng)大和便捷翅阵,好一個(gè)項(xiàng)莊舞劍歪玲,意在沛公呀。
    2. 動(dòng)態(tài)創(chuàng)建:我們知道紅框①(UITableViewCell)是支持復(fù)用的怎顾,這是毋庸置疑的读慎,但是我們知道每一條說(shuō)說(shuō)(紅框①)中包含的評(píng)論列表的個(gè)數(shù)是不一樣且Cell高度也會(huì)不一樣。這樣就會(huì)涉及到當(dāng)用戶滾動(dòng)朋友圈列表且cell復(fù)用的時(shí)候槐雾,綠框②內(nèi)部的子控件的個(gè)數(shù)也是動(dòng)態(tài)的夭委,可能增多,又可能減少募强,這樣就造成了動(dòng)態(tài)增加或刪除綠框②內(nèi)部的子控件株灸,想必大家都知道盡量不要在UITableViewCell中動(dòng)態(tài)創(chuàng)建子控件,這是比較耗性能的擎值,常規(guī)的做法都是事先在- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier一口氣創(chuàng)建所有你要顯示的控件慌烧,這樣你只需要根據(jù)數(shù)據(jù)的屬性來(lái)顯示或隱藏某個(gè)子控件即可,這樣就避免了動(dòng)態(tài)創(chuàng)建子控件的場(chǎng)景鸠儿。但是由于朋友圈列表的每條說(shuō)說(shuō)的評(píng)論列表個(gè)數(shù)是不能事先確定的屹蚊,所以必然會(huì)存在綠框②動(dòng)態(tài)創(chuàng)建子控件的悲劇。
    3. 不支持大數(shù)據(jù):對(duì)于上面動(dòng)態(tài)創(chuàng)建控件的問(wèn)題进每,其實(shí)該作者在內(nèi)部SDTimeLineCellCommentView.h/m也是做了優(yōu)化處理的汹粤,其做法主要是將動(dòng)態(tài)創(chuàng)建的子控件(UILabel)裝進(jìn)一個(gè)數(shù)組(commentLabelsArray)里面,這樣可以減少一部分的動(dòng)態(tài)創(chuàng)建子控件過(guò)程田晚,但是還是會(huì)存在動(dòng)態(tài)創(chuàng)建子控件嘱兼,其主要邏輯就是根據(jù)你傳進(jìn)來(lái)的說(shuō)說(shuō)的評(píng)論列表的個(gè)數(shù) (commentItemsArray.count)與commentLabelsArray.count比較罷了,如果前者小于等于后者贤徒,就不需要?jiǎng)討B(tài)創(chuàng)建芹壕,只是對(duì)commentLabelsArray中的子控件做顯示和隱藏處理即可汇四;反之如果前者大于后者,這需要?jiǎng)討B(tài)創(chuàng)建(commentItemsArray.count - commentLabelsArray.count)個(gè)子控件踢涌,然后又被加入到數(shù)組commentLabelsArray里面的過(guò)程通孽。關(guān)鍵代碼如下所示:
      首先微信朋友圈的評(píng)論列表的個(gè)數(shù)是支持大數(shù)據(jù)的(PS:筆者瞎猜的…),那就必須確保綠框②能支持大數(shù)據(jù)的顯示斯嚎,顯然隨著評(píng)論列表的個(gè)數(shù)逐漸增多利虫,以及UITableViewCell的不斷復(fù)用挨厚,則綠框②commentLabelsArray里面裝的子控件也會(huì)越來(lái)越多且保持只增不減的趨勢(shì)堡僻,這樣該方案就顯得比較的無(wú)力了。
    - (void)setCommentItemsArray:(NSArray *)commentItemsArray{
      _commentItemsArray = commentItemsArray;
      long originalLabelsCount = self.commentLabelsArray.count;
      long needsToAddCount = commentItemsArray.count > originalLabelsCount ? (commentItemsArray.count - originalLabelsCount) : 0;
      for (int i = 0; i < needsToAddCount; i++) {
          MLLinkLabel *label = [MLLinkLabel new];
          [self addSubview:label];
          [self.commentLabelsArray addObject:label];
        }
    }  
    

    以上就是【gsdios/GSD_WeiXin】目前筆者發(fā)現(xiàn)其存在的些許問(wèn)題以及談?wù)劰P者個(gè)人的一些理解疫剃。當(dāng)然這個(gè)方案在針對(duì)大量的評(píng)論數(shù)據(jù)的處理上或許稍有吃力钉疫,但是如果當(dāng)評(píng)論列表的個(gè)數(shù)是固定,例如:優(yōu)酷視頻的評(píng)論回復(fù)(如下圖)巢价。這個(gè)方案也不失為一個(gè)好的解決方案牲阁。所以說(shuō)業(yè)務(wù)場(chǎng)景不同,實(shí)現(xiàn)方案不同壤躲,可見(jiàn)在敲代碼之前城菊,先思考后確定實(shí)現(xiàn)方案是多么重要。

    YouKu_UI.png

  • 方案二 【zhengwenming/WeChat
    該方案將綠框②的控件選取的是一個(gè)UITableView碉克,也就是說(shuō)Cell(紅色框①)里面嵌套了一個(gè)UITableView凌唬,其內(nèi)部子控件就是UITableViewCell來(lái)處理,后面的處理其實(shí)就跟我們平常處理UITableView的方法一樣漏麦,創(chuàng)建TableView客税,遵守協(xié)議,實(shí)現(xiàn)協(xié)議方法… 撕贞,可能會(huì)不習(xí)慣的就是平常創(chuàng)建的TableView更耻,我們都是將其添加在控制器的View上,這里只是添加在UITableViewCell上罷了捏膨,其他并無(wú)差異秧均。內(nèi)部實(shí)現(xiàn)說(shuō)到底其實(shí)就是充分利用UITableView的特性,選取不同UITableViewCell來(lái)顯示點(diǎn)贊列表評(píng)論列表而已号涯,相比于方案一來(lái)說(shuō)目胡,該方案主要發(fā)揮出了UITableView的特性,通過(guò)實(shí)現(xiàn)UITableView的協(xié)議方法就能實(shí)現(xiàn)評(píng)論和點(diǎn)贊列表的展示诚隙,且實(shí)現(xiàn)起來(lái)更加簡(jiǎn)單易懂讶隐,這可能是目前市場(chǎng)上絕大多數(shù)的做法。雖然外表看似毫無(wú)破綻久又,但是其中隱藏巨大弊端巫延。之前筆者也利用這種方案效五,寫過(guò)類似微信朋友圈的評(píng)論回復(fù),詳情請(qǐng)參考:iOS 實(shí)現(xiàn)微信朋友圈評(píng)論回復(fù)功能(二)炉峰,但是其中存在的問(wèn)題畏妖,筆者卻沒(méi)有敘述,實(shí)屬抱歉疼阔,當(dāng)然這里筆者將詳述其存在弊端和產(chǎn)生的原因戒劫,以及讓大家重新加深對(duì)UITableView的理解。弊端如下:

    1. 復(fù)用問(wèn)題: 若想保證UITableView滾動(dòng)流暢婆廊,縱享絲滑迅细,就離不開UITableViewCell的復(fù)用機(jī)制(PS:這個(gè)復(fù)用機(jī)制想必大家應(yīng)該已經(jīng)滾瓜爛熟了,這里筆者就不在贅述)淘邻,這也是UITableView的核心所在茵典。首先正常情況下,我們可以確定的是紅色框①這個(gè)UITableViewCell是能夠Cell復(fù)用的宾舅,這個(gè)應(yīng)該是毫無(wú)爭(zhēng)議的统阿。但是紅色框①內(nèi)部嵌套的綠色框②這個(gè)TableView中,其內(nèi)部顯示評(píng)論數(shù)據(jù)的UITableViewCell是否也是支持Cell的復(fù)用機(jī)制呢筹我?扶平??可能大家的第一印象就是覺(jué)得是能的蔬蕊。但是這里筆者強(qiáng)調(diào)的是 綠色框②CommentCell是不支持復(fù)用的=岢巍!袁串!大家認(rèn)為CommentCell能夠復(fù)用的概而,都是認(rèn)為其復(fù)用機(jī)制完全跟紅色框①(MommentCell)復(fù)用機(jī)制一樣,都是會(huì)隨著用戶滑動(dòng)的朋友圈列表囱修,MommentCell 和 CommentCell離開都會(huì)完全離開屏幕赎瑰,然后將完全離開屏幕的MommentCell 和 CommentCell存入緩存池,等到要顯示Cell的時(shí)候又去緩存池根據(jù)reuseIdentifier去取MommentCell 和 CommentCell破镰,如果取得到餐曼,就直接拿來(lái)用;如果取不到鲜漩,就去創(chuàng)建等過(guò)程....源譬,這里筆者只能說(shuō)cell復(fù)用的概念倒是背的的挺熟,但是Cell復(fù)用的機(jī)制卻不夠理解孕似。原因是:* 之所以紅色框①這個(gè)MomentCell能夠遵循Cell復(fù)用的機(jī)制踩娘,是因?yàn)槭紫绕渌幵诘?code>UITableView的尺寸大小是和屏幕尺寸大小一致,其次朋友圈列表能夠滑動(dòng)的前提就是保證該TableView的內(nèi)容高度大于TableView的高度喉祭,即tableView.contentSize.height > tableView.frame.size.height养渴,需要強(qiáng)調(diào)的是:Cell能否產(chǎn)生復(fù)用取決于所處的tableView能否滾動(dòng)雷绢,②并且Cell能夠隨著列表滾動(dòng)完全離開所處的TableView的顯示范圍。結(jié)合這兩點(diǎn)必要條件理卑,很快可以推斷出紅色框①這個(gè)UITableViewCell是能夠滿足Cell復(fù)用的條件的翘紊。接著我們帶著這兩個(gè)必要條件來(lái)分析一下綠色框②這個(gè)TableView,首先明確的是藐唠,該TableView的高度是根據(jù)評(píng)論列表中每個(gè)評(píng)論內(nèi)容(CommentCell)的高度總和(PS:tableView.height = cell0.height+cell1.height+cell2.height ...)帆疟,這樣就導(dǎo)致了該tableView的內(nèi)容高度等于tableView的尺寸高度,即(tableView.frame.size.height = tableView.contentSize.height)宇立,所以評(píng)論列表是不會(huì)滾動(dòng)的踪宠,這樣就不滿足條件①;其次泄伪,其tableView內(nèi)部的CommentCell相對(duì)于所處的tableView的顯示區(qū)域是完全暴露的殴蓬,根本不滿足條件②,所以最終真相大白蟋滴,水落石出了,是不是豁然開朗痘绎,心情舒暢津函。 當(dāng)然這里筆者友情提醒廣大開發(fā)者千萬(wàn)不要誤認(rèn)為,只要Cell看不見(jiàn)就一定會(huì)產(chǎn)生復(fù)用的誤區(qū)孤页,主要是要明確該Cell相對(duì)于所處的TableView的顯示區(qū)域是否看不見(jiàn)尔苦。(PS:知識(shí)點(diǎn)有木有),當(dāng)然大家可以跑跑筆者寫的這篇文章:iOS 實(shí)現(xiàn)微信朋友圈評(píng)論回復(fù)功能(二)所提供的Demo行施,來(lái)驗(yàn)證一下筆者的這一說(shuō)法允坚。最后,如果綠色框②這個(gè)TableView一旦失去了Cell復(fù)用機(jī)制蛾号,用腳趾頭想想也知道稠项,那造成的后果務(wù)必會(huì)重蹈方案一存在的三個(gè)弊端的悲劇,這里筆者就不再贅述了鲜结,且筆者個(gè)人認(rèn)為整體性能還不如方案一的展运。
  • 方案三 【CoderMikeHe/WeChat
    該方案正是筆者目前使用的方案,該方案不僅很好的解決了方案一方案二目前存在的弊端精刷,而且使用起來(lái)極其簡(jiǎn)單方便以及性能優(yōu)化上更是前兩個(gè)方案無(wú)法比擬的拗胜,當(dāng)然最主要的還是考察技巧性(黑魔法)。首先筆者在認(rèn)定該方案之前怒允,前期筆者是做了大量的準(zhǔn)備工作埂软,以及仔細(xì)琢磨了紅色框①(PS:類似一條說(shuō)說(shuō))這個(gè)整體的子模塊組成。當(dāng)然必須明確的是微信朋友圈的需求:綠色框②能夠展示大量的評(píng)論數(shù)據(jù)(即:評(píng)論內(nèi)容列表的個(gè)數(shù)>=100 纫事,雖然我們會(huì)很少看到某個(gè)人的某條說(shuō)說(shuō)勘畔,有100多個(gè)人的評(píng)論內(nèi)容迷殿,而且微信的朋友圈信息流動(dòng)性非常快咖杂,這種大數(shù)據(jù)的產(chǎn)生會(huì)很少發(fā)生庆寺,但是這種大數(shù)據(jù)不代表沒(méi)有)。①考慮到微信朋友圈這一個(gè)硬需求诉字,筆者著重從性能上出發(fā)懦尝,第一想到的就是利用Cell的復(fù)用機(jī)制來(lái)展示每條說(shuō)說(shuō)的評(píng)論內(nèi)容;②考慮到前兩個(gè)方案都是把紅色框①當(dāng)做一個(gè)整體來(lái)處理壤圃,且都來(lái)了類似的弊端以及針對(duì)評(píng)論內(nèi)容大數(shù)據(jù)所帶來(lái)的性能問(wèn)題陵霉,以免重蹈覆轍,筆者將紅色框①拆分為下圖幾個(gè)模塊:一條說(shuō)說(shuō)(紅色框①) = 組(段)頭(綠色框②) + Cell(紫色框③) + 組(段)尾(黑色框④)伍绳。

    Moment_Plan3_UI.jpeg

通過(guò)上圖所示踊挠,雖然該方案在模塊劃分上是比較的分散,但是其總體帶來(lái)的性能是非吵迳保客觀的效床,大大保證了朋友圈列表滾動(dòng)的流暢性。其中當(dāng)然最最主要的原因還是歸功于上圖所示的組(段)頭(綠色框②)权谁、Cell(紫色框③)剩檀、組(段)尾(黑色框④)這三個(gè)控件都是可以通過(guò)使用TableView的數(shù)據(jù)源方法以及代理方法(代碼如下)輕松實(shí)現(xiàn)View的復(fù)用機(jī)制的,而且都是平常開發(fā)中常用的方法旺芽,這樣前面兩個(gè)方案所存在的弊端就迎刃而解了沪猴。

/// UITableViewDelegate
/// 組(段)頭
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
/// 組(段)尾
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;

/// UITableViewDataSource
/// Cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

當(dāng)然采章,組(段)頭运嗜、組(段)尾的內(nèi)部控件布局,想必對(duì)于大家已經(jīng)是手到擒來(lái)的東西悯舟,這里筆者也不過(guò)多討論担租,詳情請(qǐng)參考筆者提供的Demo,自行領(lǐng)會(huì)图谷。這里筆者主要想說(shuō)的就是Cell(紫色框③)翩活,首先平常開發(fā)過(guò)程中,Cell的寬度一般是跟所處的tableView的寬度是一致的便贵,但是微信朋友圈里面的這個(gè)評(píng)論Cell明顯不是菠镇,這里筆者需要強(qiáng)調(diào)的是:重寫是個(gè)好東西。這里的關(guān)鍵點(diǎn)就是在于重寫自定義的UITableViewCell- (void)setFrame:(CGRect)frame方法承璃,關(guān)鍵代碼如下:

/// PS:重寫cell的設(shè)置尺寸的方法利耍, 這是評(píng)論View關(guān)鍵
- (void)setFrame:(CGRect)frame{
    frame.origin.x = MHMomentContentLeftOrRightInset+MHMomentAvatarWH+MHMomentContentInnerMargin;
    frame.size.width = MHMomentCommentViewWidth();
    [super setFrame:frame];
}

當(dāng)然對(duì)于這種方案(組(段)頭+Cell+組(段)尾)的實(shí)現(xiàn)過(guò)程,筆者以前就寫過(guò)一篇文章,來(lái)詳細(xì)介紹這其中的關(guān)鍵點(diǎn)隘梨,詳情請(qǐng)參考:iOS 實(shí)現(xiàn)微信朋友圈評(píng)論回復(fù)功能(一)程癌。最后,筆者個(gè)人認(rèn)為這個(gè)方案目前是實(shí)現(xiàn)類似微信朋友圈這種支持無(wú)限評(píng)論需求的最優(yōu)雅的實(shí)施方案轴猎。
當(dāng)然還有一種方案就是:微信官方團(tuán)隊(duì)做朋友圈開發(fā)的實(shí)現(xiàn)方案嵌莉。如果這篇文章能夠有幸被微信的開發(fā)人員看到,也請(qǐng)微信的開發(fā)人員分享一下微信官方的朋友圈的實(shí)現(xiàn)方案哦捻脖;或者如果筆者的這個(gè)方案正好和微信官方的如出一轍锐峭,那么也請(qǐng)為筆者瘋狂打Call(權(quán)威認(rèn)證)哦。最后筆者希望這篇文章能夠?yàn)榇蠹医獬┰S疑惑可婶,帶來(lái)些許幫助沿癞。

功能

分析開發(fā)筆者在微信朋友圈時(shí)多遇到一些比較需要技巧性的功能模塊的實(shí)現(xiàn)以及細(xì)節(jié)處理,當(dāng)然實(shí)現(xiàn)的方案一定不是唯一的矛渴,但是筆者的目的是希望大家能夠積極討論椎扬,然后繼續(xù)完善朋友圈的各個(gè)功能模塊。

  • 評(píng)論/回復(fù)時(shí)具温,TableView滾動(dòng)到指定的區(qū)域
    這個(gè)功能是目前小伙伴問(wèn)的最多的小功能模塊之一蚕涤,具體效果圖如下所示(PS:打開自己手機(jī)中微信朋友圈,玩弄一下):首先我們的明確該功能主要是為了避免鍵盤評(píng)論輸入框遮蓋住用戶想要評(píng)論或回復(fù)的內(nèi)容桂躏。其次微信朋友圈官方做法(需求)是:① 用戶點(diǎn)擊組(段)頭(綠色框②)上的彈出的【評(píng)論】按鈕 钻趋, 彈出鍵盤評(píng)論輸入框,我們需要保證組(段)尾(黑色框④)的底部顯示在評(píng)論輸入框的頂部剂习,且伴隨著評(píng)論輸入框高度的變化而組(段)尾(黑色框④)的底部仍舊顯示在評(píng)論輸入框的頂部; ② 用戶點(diǎn)擊評(píng)論Cell(紫色框③)彈出鍵盤評(píng)論輸入框较沪,我們需要保證評(píng)論Cell(紫色框③)的底部顯示在評(píng)論輸入框的頂部鳞绕,且伴隨著評(píng)論輸入框高度的變化而評(píng)論Cell(紫色框③)的底部仍舊顯示在評(píng)論輸入框的頂部;
    Comment.gif

    經(jīng)過(guò)上述的需求分析尸曼,我們可以明確的是:當(dāng)彈出鍵盤評(píng)論輸入框時(shí)们何,滾動(dòng)TableView到合適的區(qū)域來(lái)滿足上述的條件即可,滾動(dòng)TableView無(wú)非就是設(shè)置其contentOffset.y(PS:- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated)的值即可控轿,目前最關(guān)鍵的就是計(jì)算出:需要計(jì)算出contentOffset.y的值即可冤竹。
    筆者僅以上面的需求①為例,來(lái)說(shuō)說(shuō)筆者的思路和做法茬射,首先如果彈出鍵盤評(píng)論輸入框時(shí)鹦蠕,TableView不設(shè)置合適contentOffset,則UI效果無(wú)非是以下三種情況:(PS:紅色分割線代表鍵盤評(píng)論輸入框彈出時(shí)在抛,評(píng)論輸入框的頂部所處的位置 钟病, 其他顏色的分割線代表組(段)尾(黑色框④)的底部位置)
場(chǎng)景一 場(chǎng)景二 場(chǎng)景三
Moment_UI_Up.png
Moment_UI_NO.png
Moment_UI_Down.png
delta = Y1 - Y2 < 0 delta = Y1 - Y2 = 0 delta = Y1 - Y2 > 0

場(chǎng)景一:該情況如果不設(shè)置TableView滾動(dòng),那么勢(shì)必會(huì)導(dǎo)致鍵盤評(píng)論輸入框遮蓋住評(píng)論內(nèi)容(PS:藍(lán)色分割線),從而影響用戶體驗(yàn)肠阱;如果想要顯示出評(píng)論內(nèi)容票唆,只需要讓TableView向上(delta < 0)滾動(dòng)abs(Delta)距離。
場(chǎng)景二: 該情況屬于理想狀態(tài)屹徘,TableView無(wú)需上下滾動(dòng)走趋。
場(chǎng)景三:該情況若不處理,則會(huì)導(dǎo)致要評(píng)論的內(nèi)容噪伊,距離評(píng)論輸入框很遠(yuǎn)簿煌,會(huì)讓用戶懷疑這條評(píng)論究竟是評(píng)論誰(shuí)的,從而影響用戶體驗(yàn)酥宴。為了達(dá)到需求啦吧,則需要讓TableView向下(delta > 0)滾動(dòng)Delta距離即可。
想必通過(guò)上表的畫圖分析拙寡,想必大家已經(jīng)胸有成竹了吧授滓。目前最主要的是:如何計(jì)算出Delta = Y1 - Y2的值?
首先我們應(yīng)該知道,評(píng)論輸入框組(段)尾(黑色框④)處于不同的坐標(biāo)系肆糕,不能直接計(jì)算般堆,所以,關(guān)鍵點(diǎn)就是我們必須將組(段)尾(黑色框④)的坐標(biāo)系轉(zhuǎn)換成和評(píng)論輸入框一樣的坐標(biāo)系诚啃,其次再設(shè)置TableViewcontentOffset.y = contentOffset.y + delta即可淮摔。需求②也采用類似的方法來(lái)處理即可,這里筆者不再贅述始赎,詳情見(jiàn)Demo和橙,關(guān)鍵代碼如下所示:

/// 評(píng)論的時(shí)候 滾動(dòng)tableView
- (void)_scrollTheTableViewForComment{
    CGRect rect = CGRectZero;
    CGRect rect1 = CGRectZero;
    if (self.selectedIndexPath.row == -1) {
        /// 獲取整個(gè)尾部section對(duì)應(yīng)的尺寸 獲取的rect是相當(dāng)于tableView的尺寸
        rect = [self.tableView rectForFooterInSection:self.selectedIndexPath.section];
        /// 將尺寸轉(zhuǎn)化到window的坐標(biāo)系 (關(guān)鍵點(diǎn))
        rect1 = [self.tableView convertRect:rect toViewOrWindow:nil];
    }else{
        /// 回復(fù)
        /// 獲取整個(gè)尾部section對(duì)應(yīng)的尺寸 獲取的rect是相當(dāng)于tableView的尺寸
        rect = [self.tableView rectForRowAtIndexPath:self.selectedIndexPath];
        /// 將尺寸轉(zhuǎn)化到window的坐標(biāo)系 (關(guān)鍵點(diǎn))
        rect1 = [self.tableView convertRect:rect toViewOrWindow:nil];
    }
    if (self.keyboardHeight > 0) { /// 鍵盤抬起 才允許滾動(dòng)
        /// 這個(gè)就是你需要滾動(dòng)差值
        CGFloat delta = self.commentToolView.mh_top - rect1.origin.y - rect1.size.height;
        [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-delta) animated:NO];
    }
}
未完...待續(xù)...(PS:不說(shuō)再見(jiàn),我們后會(huì)有期)

本篇文章筆者主要是分析實(shí)現(xiàn)微信朋友圈的最優(yōu)方案造垛,希望能為大家在敲代碼之前樹立一個(gè)正確的參考魔招,這樣能夠避免大家走許多彎路。當(dāng)然微信朋友圈的技術(shù)要點(diǎn)和技術(shù)細(xì)節(jié)五辽,雖然看似簡(jiǎn)單办斑,但是細(xì)節(jié)處理非常重要,筆者在接下來(lái)的時(shí)間內(nèi)杆逗,會(huì)陸續(xù)為其增加更多功能模塊乡翅,以及將在開發(fā)WeChat朋友圈中用到的好用技術(shù)以及細(xì)節(jié)處理分享出來(lái),希望提供大家一個(gè)參考罪郊,爭(zhēng)取能為大家答疑解惑蠕蚜。當(dāng)然也希望大家踴躍發(fā)言,共同交流排龄,共同進(jìn)步波势。

期待
  1. 文章若對(duì)您有些許幫助翎朱,請(qǐng)給個(gè)喜歡??,畢竟碼字不易尺铣;若對(duì)您沒(méi)啥幫助拴曲,請(qǐng)給點(diǎn)建議??,切記學(xué)無(wú)止境凛忿。
  2. 針對(duì)文章所述內(nèi)容澈灼,閱讀期間任何疑問(wèn);請(qǐng)?jiān)谖恼碌撞吭u(píng)論指出店溢,我會(huì)火速解決和修正問(wèn)題叁熔。
  3. GitHub地址:https://github.com/CoderMikeHe
  4. 源碼地址:WeChat
參考鏈接
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市床牧,隨后出現(xiàn)的幾起案子荣回,更是在濱河造成了極大的恐慌,老刑警劉巖戈咳,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件心软,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡著蛙,警方通過(guò)查閱死者的電腦和手機(jī)删铃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)踏堡,“玉大人猎唁,你說(shuō)我怎么就攤上這事∏牦。” “怎么了诫隅?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)帐偎。 經(jīng)常有香客問(wèn)我阎肝,道長(zhǎng),這世上最難降的妖魔是什么肮街? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮判导,結(jié)果婚禮上嫉父,老公的妹妹穿的比我還像新娘。我一直安慰自己眼刃,他們只是感情好绕辖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著擂红,像睡著了一般仪际。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天树碱,我揣著相機(jī)與錄音肯适,去河邊找鬼。 笑死成榜,一個(gè)胖子當(dāng)著我的面吹牛框舔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赎婚,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼刘绣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挣输?” 一聲冷哼從身側(cè)響起纬凤,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撩嚼,沒(méi)想到半個(gè)月后停士,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绢馍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年向瓷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舰涌。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猖任,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓷耙,到底是詐尸還是另有隱情朱躺,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布搁痛,位于F島的核電站长搀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鸡典。R本人自食惡果不足惜源请,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望彻况。 院中可真熱鬧谁尸,春花似錦、人聲如沸纽甘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)悍赢。三九已至决瞳,卻和暖如春货徙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背皮胡。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工痴颊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胸囱。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓祷舀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親烹笔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裳扯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355