微博的角標"...全文"功能是如何實現(xiàn)的

寫在前面

文章詳情頁
特殊文字是一次性刪除的
點擊特殊文字掖棉,會有響應
折行效果

公司最近寫了一個社交的功能模塊,有以下幾個功能
1.發(fā)布文章
2.轉發(fā)文章
3.評論文章
4.回復評論
5.文章列表
基本上就是公司一切仿照微博的寫近她,然后我就一直寫啊寫~


這個其實是比較簡單的功能,稍微麻煩一點的是富文本技術愤兵,

1.如列表頁點擊@張同學铁蹈,點擊$深圳股指(000001)$#我是個話題#等等
2.要判斷文章是不是超過七行文字拉队,如果超過了弊知,那么我們要有...全文來結尾
3.在發(fā)布頁面,當打出來特殊的字符粱快,如@張同學秩彤,點擊$深圳股指(000001)$等,將他們作為一個整體事哭,然后刪除的時候漫雷,將一個整體刪除
4.如果是超鏈接的話,那么可能有折行問題鳍咱,如果點擊了某個折行降盹,整個超鏈接都要響應,如何處理谤辜?


過去因為沒有處理過這些澎现,所以一點點的學習,這里真心挺多坑每辟,最后很多API都是底層的剑辫,找起來很累,但是好在實現(xiàn)了渠欺,在網(wǎng)上找了題目的問題妹蔽,但是沒有,很朋友一起討論,給了我一個臨時的方法胳岂,就是如果文本超過了七行编整,直接將...全文貼到第七行上,都不用看乳丰,這樣很簡單掌测,但是效果很不好,所以我就用了一天的時間探索這個問題产园,好在最后汞斧,找出了答案


這個項目使用了TextKit的相關技術,然后我才看到了有好多底層的東西什燕,過去習慣寫UI了粘勒,所以真心沒有研究過底層,之前有看過YYKit的東西屎即,但是沒有自己寫庙睡,所以最近趁著學習textkit,趁機寫一個像錘子便簽的那個圖文混排的demo技俐,并且乘陪,錘子便簽支持富文本,這個真心贊雕擂,所以決定好好學習一下~


說點正題

"...全文"這個功能相對比較麻煩暂刘,因為他可能要考慮好幾種情況,最后還要有api才能實現(xiàn)捂刺,我因為剛剛接觸谣拣,所以很多地方是不確定的,所以如果有同行知道更好的解決方案族展,麻煩告訴我一下森缠,我也好好的學習。一會pod出來的代碼仪缸,是沒有經(jīng)過重構的贵涵,性能的就不考慮了,一會會寫出來具體的優(yōu)化思路恰画。將來有時間了宾茂,我才去重構一下,然后再拿出來一起看看

思維導圖

圖例14.為什么要獲取第七行的frame

先說說思路
1.獲取第七行文字
2.生成一個控件 "...全文"拴还,寬度50dx
3.判斷最后一個字符是不是在富文本中跨晴,如果在就刪除他,和4放在一起使用
4.然后去判斷 刪除一個字節(jié)之后的第七行文字+"...全文" <textView寬度(但是如果剛才刪除的最后一個字符在特殊文本之內片林,如@張同學端盆,點擊$深圳股指(000001)$怀骤,那么我們應該直接將這個字符全都刪除,再去判斷5是否成立)焕妙,for循環(huán)蒋伦,知道for的次數(shù)是第七行文字的length
6.for完畢之后,我們獲取出一個第七行最合適的文本焚鹊,那么我們應該去將這個最合適的文本替換給 textView.attributedText的第七行文字痕届,然后去更新一下...全文的x值
7.一定要注意,就是防止循環(huán)引用末患,很多屬性都要給status研叫,然后拿到了才不會有問題,我這寫在view中阻塑,所以view經(jīng)常會被復用蓝撇,導致了一些屬性有問題果复,切記


具體實現(xiàn)的代碼如下:

     self.textLabel.frame = originalFrame.textFrame;
    //設置超過一定行數(shù)陈莽,就截斷的功能,防止循環(huán)引用
    if(status.isNeed){
        BOOL hiddenMore = (self.originalFrame.textFrame.size.height < SEStatusLableMaxRowHeight);
        self.textLabel.moreView.hidden = hiddenMore;
        [self.textLabel layoutSubviews];
        if (!hiddenMore && (self.originalFrame.allTextX == 0)) {
            CGRect lastRowFrame = CGRectMake(0, 108, ScreenWidth - 2*SEStatusCellInset, originalFrame.textFrame.size.height);
            NSRange lastRowRangeInAllText = [self.textLabel.textView.layoutManager glyphRangeForBoundingRect:lastRowFrame inTextContainer:self.textLabel.textView.textContainer];
            //通過lastRowRangeInAllText獲取最后一行的文字
            NSString *lastRowString = [self.textLabel.textView.attributedText.string substringWithRange:lastRowRangeInAllText];
            //最后一行文字的size
            CGSize lastRowTextSize = [self fetchTextWidthWithTextString:lastRowString andFrame:lastRowFrame];
            BOOL lastRowTextFit = [self judgeWhetherIsRightWithLastRowSize:lastRowTextSize andFrame:lastRowFrame];
            if(lastRowTextFit){ //50是"...全部"的寬度,10虽抄,文字和 ...全文 的間隔
                //判斷最后一個字符的loction是不是在某個富文本之中
                //如果在其中走搁,只能是是最后,為真迈窟,其他為假
                //如果不在私植,可以直接添加了
                [self.textLabel.attributedText enumerateAttributesInRange:NSMakeRange(0, self.textLabel.attributedText.length)
                                                                  options:NSAttributedStringEnumerationReverse usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
                                                                      if(self.originalFrame.status.stopEnumerate){
//                                                                          return ;
                                                                          return ;
                                                                      }
                                                                      NSString *linkText = attrs[SELinkText];
                                                                      NSInteger linkLoctionInLastRowText = range.location-lastRowRangeInAllText.location;
                                                                      if(linkText == nil){
                                                                          //如果符合,我們直接給 ... 全部 一個x值就好了
                                                                          self.originalFrame.allTextX = lastRowTextSize.width + SEAllTextMarginToRemindText;
                                                                          self.textLabel.moreX = self.originalFrame.allTextX;
                                                                          return;
                                                                      }
                                                                      if(linkLoctionInLastRowText<0){
                                                                          return;
                                                                      }
                                                                      
                                                                      //保存最有一個富文本,也是有問題的车酣,如果在第九行曲稼,也是白搭
                                                                      //看看一共包含
                                                                      NSInteger numElements = range.length/linkText.length;
                                                                      for(int j = 0;j<numElements;j++){
                                                                          //臨時的位置
                                                                          NSInteger tempLinkLoctionInLastRowText =  linkLoctionInLastRowText + linkText.length*j;
                                                                          NSRange linkRange = NSMakeRange(tempLinkLoctionInLastRowText, linkText.length);
                                                                          if(linkRange.location != NSNotFound && linkRange.length){
                                                                              NSInteger maxRangeLoc = NSMaxRange(linkRange)-1;
                                                                              NSInteger lastWordLoc = lastRowString.length-1;
                                                                                  if(maxRangeLoc == lastWordLoc){
                                                                                      //如果符合,我們直接給 ... 全部 一個x值就好了
                                                                                      self.originalFrame.allTextX = lastRowTextSize.width + SEAllTextMarginToRemindText;
                                                                                      self.textLabel.moreX = self.originalFrame.allTextX;
                                                                                      return ;
                                                                                  }else{
                                                                                      //刪除數(shù)據(jù)了
                                                                                      [self deleteLastWordWithLastRowString:lastRowString
                                                                                                      lastRowRangeInAllText:lastRowRangeInAllText
                                                                                                               lastRowFrame:lastRowFrame];
                                                                                  }
                                                                              }
                                                                          //感覺這里要做點事情湖员,但是沒有想好
                                                                          }
                                                                      }
                                                                  ];
            }
            else{
                
                [self deleteLastWordWithLastRowString:lastRowString
                                lastRowRangeInAllText:lastRowRangeInAllText
                                         lastRowFrame:lastRowFrame];
            }
        }
        else if (!hiddenMore && self.originalFrame.allTextX != 0){
            self.textLabel.moreX = self.originalFrame.allTextX;
        }
    }
    else{
        self.textLabel.moreView.hidden = YES;
    }
    self.textLabel.moreView.hidden = (self.originalFrame.allTextX==0);
//刪除最后一個字符贫悄,判斷是否符合要求
- (void)deleteLastWordWithLastRowString:(NSString *)lastRowString
                  lastRowRangeInAllText:(NSRange)lastRowRangeInAllText
                           lastRowFrame:(CGRect)lastRowFrame
{
    for(int i=1;i<=lastRowString.length;i++){
        //往前減去一個字符,判斷是不是特殊字符娘摔,如果是特殊字符窄坦,算出將整個特俗字符刪除,再去計算凳寺,判斷
        __block NSRange lastWordRangeInRow = NSMakeRange(lastRowString.length-i, 1);
        //保存一個當前最后一行數(shù)據(jù)鸭津,就是個臨時的東西
        __block NSString *tempLastString = nil;
        
        [self.textLabel.attributedText enumerateAttributesInRange:NSMakeRange(0, self.textLabel.attributedText.length)
                                                          options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
                                                              
                                                              NSString *linkText = attrs[SELinkText];
                                                              //如果沒有富文本,反悔肠缨,然后直接長度減一
                                                              if (linkText == nil) return;
                                                              NSInteger linkLoctionInLastRowText = range.location-lastRowRangeInAllText.location;
                                                              if(linkLoctionInLastRowText<0)
                                                                  return;
                                                              //看看一共包含
                                                              NSInteger numElements = range.length/linkText.length;
                                                              for(int j = 0;j<numElements;j++){
                                                                  //最有一個位置
                                                                  NSInteger tempLinkLoctionInLastRowText =  linkLoctionInLastRowText + linkText.length*j;
                                                                  NSRange linkRange = NSMakeRange(tempLinkLoctionInLastRowText, linkText.length);
                                                                  if(linkRange.location != NSNotFound && linkRange.length){
                                                                      BOOL same = NSLocationInRange(lastWordRangeInRow.location, linkRange);
                                                                      if(same){
                                                                          lastWordRangeInRow = linkRange;
                                                                          break;
                                                                      }else if(NSEqualRanges(lastWordRangeInRow, linkRange)){
                                                                          lastWordRangeInRow = NSMakeRange(linkRange.location-1, 1);
                                                                          break;
                                                                      }else{
                                                                          //啥也不干
                                                                      }
                                                                  }
                                                              }
                                                          }];
        
        
        //將最后的字符刪除
        tempLastString = [lastRowString substringToIndex:lastWordRangeInRow.location];
        //計算一下新的字符串是否符合『...全文』的寬度
        CGSize tempSize = [self fetchTextWidthWithTextString:tempLastString andFrame:lastRowFrame];
        BOOL tempSmaller = [self judgeWhetherIsRightWithLastRowSize:tempSize andFrame:lastRowFrame];
        if(tempSmaller){ //可以放下
            //獲取最后一個字符以后的文字的range
            NSInteger lastWordLoctionInTextView = lastRowRangeInAllText.location+tempLastString.length;
            NSInteger willRemoveTextLength = self.textLabel.textView.attributedText.length - lastWordLoctionInTextView;
            NSRange willRemoveRange = NSMakeRange(lastWordLoctionInTextView, willRemoveTextLength);
            NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textLabel.textView.attributedText];
            //使用刪除或者替換 delete...都可以
            [mutableString replaceCharactersInRange:willRemoveRange withString:@""];
            self.textLabel.textView.attributedText = mutableString;
            self.originalFrame.status.attributedText = mutableString;
            
            //給"...全部"一個frame
            self.originalFrame.allTextX = tempSize.width + SEAllTextMarginToRemindText;
            self.textLabel.moreX = self.originalFrame.allTextX;
            self.originalFrame.status.stopEnumerate = YES;
            break;
        }
        else{
            continue;
        }
    }
}
圖1.首頁列表顯示的樣子
圖2.點擊進入詳情頁

這兩張圖片要說明什么逆趋?就是在數(shù)據(jù)返回來的時候,我們給他排班晒奕,最后一行在詳情頁就可以看到了父泳,這種情況下般哼,添加『..全文』是成立的,所以就像圖1.那樣惠窄,當時他確實錯誤的蒸眠,所以應該直接去判斷最后一個字符是不是在富文本中,然后再判斷如果加了"...全文"杆融,是不是可以符合寬度的要求

“...全文”的位置都是正確的
同一個cell楞卡,內部和外邊比較,截取的都是一個特殊的字符串的尺寸
普通文本截取的時候也是正確合理的

項目缺點
第一.i值可能多次使用脾歇,如果刪除特殊的字符串的時候,如@zhang,那么下一次i = i-zhang.length
第二.每一次都要計算一下蒋腮,應該是去緩存下來,"...全文"的尺寸只去計算一次
第三.這個代碼很垃圾,所以我們有時間重構一下藕各,封裝一個類

這個是我自己寫的東西池摧,感覺很多的地方是不好的,性能什么的激况,如果諸位知道如何做作彤,一定要告訴我哈~一起學習進步


今天領導突然要更改文字的大小和文字間距,我就蒙了乌逐,然后去這里"...全文"的功能就亂了竭讳,然后去修改

/**
  *七行文字的問題,當字體是14dx浙踢,行間距是4dx(設計圖中是16px绢慢,對應到項目中是4dx,因為有上下行的問題洛波,所以除以4)的時候胰舆;
  * textView適合的高度(能在textView中顯示7行文字的高度)
  * 如果文字小于七行,也是要+2dx(已經(jīng)處理了)蹬挤,如果大于的時候缚窿,也處理了(在SEStatusLableMaxRowHeight中最合適的+2了)
  * SEStatusLableMaxRowHeight 數(shù)據(jù)是如何計算的?通過計算獲取textSize的高度+2闻伶,這個是第七行和文字比較的系數(shù)滨攻,比較是合理的,但是此時textView如果是8行的時候蓝翰,通過layoutManager獲取不到第七行文字光绕,要再去+2才行,也是就是+4,(我說的是14dx畜份,4dx的情況诞帐,如果是16號字體不確定,但是核心是保證獲取到第七行文字)
  *7行文字textView適合的高度 如何確定爆雹?當前文字高度+2 (我說的是14dx停蕉,4dx的情況)
 
 
  *    字體大小      行間距   第7行文字的高度   第8行文字的高度    7行文字textView適合的高度  最后一行文字的frame    比較第七行的系數(shù)
  *      14dx       4dx      141             161.6               145                (0,129,355,145)              145
  *      16dx       4dx      157.6           180.7               160             (0,136,355,145)              162
 */

修改后的樣式
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末愕鼓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子慧起,更是在濱河造成了極大的恐慌菇晃,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚓挤,死亡現(xiàn)場離奇詭異磺送,居然都是意外死亡,警方通過查閱死者的電腦和手機灿意,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門估灿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缤剧,你說我怎么就攤上這事馅袁。” “怎么了荒辕?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵汗销,是天一觀的道長。 經(jīng)常有香客問我兄纺,道長大溜,這世上最難降的妖魔是什么化漆? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任估脆,我火速辦了婚禮,結果婚禮上座云,老公的妹妹穿的比我還像新娘疙赠。我一直安慰自己,他們只是感情好朦拖,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布圃阳。 她就那樣靜靜地躺著,像睡著了一般璧帝。 火紅的嫁衣襯著肌膚如雪捍岳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天睬隶,我揣著相機與錄音锣夹,去河邊找鬼。 笑死苏潜,一個胖子當著我的面吹牛银萍,可吹牛的內容都是我干的。 我是一名探鬼主播恤左,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼贴唇,長吁一口氣:“原來是場噩夢啊……” “哼搀绣!你這毒婦竟也來了?” 一聲冷哼從身側響起戳气,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤链患,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瓶您,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體者蠕,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡携兵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹂空。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宦赠,靈堂內的尸體忽然破棺而出骤坐,到底是詐尸還是另有隱情,我是刑警寧澤油吭,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布击蹲,位于F島的核電站,受9級特大地震影響婉宰,放射性物質發(fā)生泄漏歌豺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一心包、第九天 我趴在偏房一處隱蔽的房頂上張望类咧。 院中可真熱鬧,春花似錦蟹腾、人聲如沸痕惋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽值戳。三九已至,卻和暖如春炉爆,著一層夾襖步出監(jiān)牢的瞬間堕虹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工芬首, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赴捞,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓衩辟,卻偏偏與公主長得像螟炫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子艺晴,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內容

  • 基礎命令 主要的命令和快捷鍵 Linux系統(tǒng)命令由三部分組成:cmd + [options]+[operation...
    485b1aca799e閱讀 1,104評論 0 0
  • 周一學習會昼钻,學習“利他”這兩字書中提到好多次掸屡,其實就是在提醒我們的大家要懂得感恩、懂得回饋社會然评,佛家的“因果報應”...
    周晨i閱讀 227評論 0 0
  • 沒有人真正的希望你過得好 這話不假 心中感慨萬千 反過來想 確實如此 總之 過好自己的生活 不惹是非 過著耳根清凈...
    優(yōu)雅老太太閱讀 202評論 0 0
  • 在不久前日本《讀賣新聞》做的一項調查中顯示碗淌,日本和中國分別有56%和44%不喜歡對方盏求,但不喜歡并不能抹殺對方的成就...
    茸小呆閱讀 1,206評論 0 14