UITextView的動態適應高度處理,uitextview動態適應

來源:互聯網
上載者:User

UITextView的動態適應高度處理,uitextview動態適應

本文章主要是處理自適應高度的。

想到自適應高度,想必大家都知道使用

boundingRectWithSize:options:attributes 來計算。

嗯確實,這是個利器。其本上能正確返回字型的rect。但對於UITextView 似乎使用此方法計算出來的結果比實際顯示的要小。為什麼呢?

我個人也在網上搜了好多,但都不盡人意。於是乎想是不是UITextView做了些額外的處理,比如把行高,邊框寬也加在一起了。順著這條線索一路摸,果然,找出了點門道。

如果你對UITextView熟悉的話,你知道他有個

sizeThatFits

方法,這個可以正確的返回字型顯示的RECT大小。哪有朋友就想了,既然都提供了這個方法,哪不是更好麼,省得自己去計算。不是更直接麼。好是好,但要觸保什麼場合使用。

哪麼使用sizeThatFits會出現什麼問題。


給大家舉個不太舒服的bug;

相信大家度娘時大多搜出來的代碼是這樣的。

- (void)textViewDidChange:(UITextView *)textView{    CGSize size = [textView sizeThatFits:CGSizeMake(CGRectGetWidth(textView.frame), MAXFLOAT)];    CGRect frame = textView.frame;    frame.size.height = size.height;    textView.frame = frame;}
在輸入的時候,很正常的,會自動調整度高,沒有什麼問題。但如果你採用的不是輸入,而是COPY和粘貼,哪麼問題就出現了。見圖

未輸入時高度設為35

當我複製幾行漢字時,再粘貼,看會有什麼問題。見圖。咦,處長的字型上移了一部分,當滾動一下又回到了正常顯示。


為什麼會這樣呢?原因是因為sizeThatFits是在有文字時才進行計算的。一開始為35高度。在漢字輸入完全結束後,高度還是35.由於字型過多所以要顯示全部字型時先將字型向上滾動,以顯示到最後的字元。之後再調用DidChange方法這裡由於已經有字型了,再來計算出高度。並正確設定了高度。但由於字型滾動在先。frame設定在後,且設定後沒有重新布局導致。


哪怎麼解決此問題呢。詳細經過就不講解了。代碼貼上來,大家學習一下,值得關注的是計算動態度哪個方法,希望對大家起作用。找了好多資料才摸出這個。算是比較準確的。與sizeThatFits算出來差距不大。(大字也嘗試下如果帶有emoji計算出來的結果是否有差距啊這個沒試呢。)


完整代碼:(基於上一篇的字型限制基礎上進行處理的)

- (CGSize)getStringRectInTextView:(NSString *)string InTextView:(UITextView *)textView;{    //    //    NSLog(@"行高  = %f container = %@,xxx = %f",self.textview.font.lineHeight,self.textview.textContainer,self.textview.textContainer.lineFragmentPadding);    //    //實際textView顯示時我們設定的寬    CGFloat contentWidth = CGRectGetWidth(textView.frame);    //但事實上內容需要除去顯示的邊框值    CGFloat broadWith    = (textView.contentInset.left + textView.contentInset.right                            + textView.textContainerInset.left                            + textView.textContainerInset.right                            + textView.textContainer.lineFragmentPadding/*左邊距*/                            + textView.textContainer.lineFragmentPadding/*右邊距*/);        CGFloat broadHeight  = (textView.contentInset.top                            + textView.contentInset.bottom                            + textView.textContainerInset.top                            + textView.textContainerInset.bottom);//+self.textview.textContainer.lineFragmentPadding/*top*//*+theTextView.textContainer.lineFragmentPadding*//*there is no bottom padding*/);        //由於求的是一般字元串產生的Rect來適應textView的寬    contentWidth -= broadWith;        CGSize InSize = CGSizeMake(contentWidth, MAXFLOAT);        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];    paragraphStyle.lineBreakMode = textView.textContainer.lineBreakMode;    NSDictionary *dic = @{NSFontAttributeName:textView.font, NSParagraphStyleAttributeName:[paragraphStyle copy]};        CGSize calculatedSize =  [string boundingRectWithSize:InSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:dic context:nil].size;        CGSize adjustedSize = CGSizeMake(ceilf(calculatedSize.width),calculatedSize.height + broadHeight);//ceilf(calculatedSize.height)    return adjustedSize;}- (void)refreshTextViewSize:(UITextView *)textView{    CGSize size = [textView sizeThatFits:CGSizeMake(CGRectGetWidth(textView.frame), MAXFLOAT)];    CGRect frame = textView.frame;    frame.size.height = size.height;    textView.frame = frame;}- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{    //對於退格刪除鍵開放限制    if (text.length == 0) {        return YES;    }        UITextRange *selectedRange = [textView markedTextRange];    //擷取高亮部分    UITextPosition *pos = [textView positionFromPosition:selectedRange.start offset:0];    //擷取高亮部分內容    //NSString * selectedtext = [textView textInRange:selectedRange];        //如果有高亮且當前字數開始位置小於最大限制時允許輸入    if (selectedRange && pos) {        NSInteger startOffset = [textView offsetFromPosition:textView.beginningOfDocument toPosition:selectedRange.start];        NSInteger endOffset = [textView offsetFromPosition:textView.beginningOfDocument toPosition:selectedRange.end];        NSRange offsetRange = NSMakeRange(startOffset, endOffset - startOffset);                if (offsetRange.location < MAX_LIMIT_NUMS) {            return YES;        }        else        {            return NO;        }    }        NSString *comcatstr = [textView.text stringByReplacingCharactersInRange:range withString:text];        NSInteger caninputlen = MAX_LIMIT_NUMS - comcatstr.length;        if (caninputlen >= 0)    {        //加入動態計算高度        CGSize size = [self getStringRectInTextView:comcatstr InTextView:textView];        CGRect frame = textView.frame;        frame.size.height = size.height;        textView.frame = frame;        return YES;    }    else    {        NSInteger len = text.length + caninputlen;        //防止當text.length + caninputlen < 0時,使得rg.length為一個非法最大正數出錯        NSRange rg = {0,MAX(len,0)};                if (rg.length > 0)        {            NSString *s = @"";            //判斷是否只普通的字元或asc碼(對於中文和表情返回NO)            BOOL asc = [text canBeConvertedToEncoding:NSASCIIStringEncoding];            if (asc) {                s = [text substringWithRange:rg];//因為是ascii碼直接取就可以了不會錯            }            else            {                __block NSInteger idx = 0;                __block NSString  *trimString = @"";//截取出的字串                //使用字串遍曆,這個方法能準確知道每個emoji是佔一個unicode還是兩個                [text enumerateSubstringsInRange:NSMakeRange(0, [text length])                                              options:NSStringEnumerationByComposedCharacterSequences                                           usingBlock: ^(NSString* substring, NSRange substringRange, NSRange enclosingRange, BOOL* stop) {                                                                                              NSInteger steplen = substring.length;                                               if (idx >= rg.length) {                                                   *stop = YES; //取出所需要就break,提高效率                                                   return ;                                               }                                                                                              trimString = [trimString stringByAppendingString:substring];                                                                                              idx = idx + steplen;                                           }];                                s = trimString;            }            //rang是指從當前游標處進行替換處理(注意如果執行此句後面返回的是YES會觸發didchange事件)            [textView setText:[textView.text stringByReplacingCharactersInRange:range withString:s]];                        //由於後面反回的是NO不觸發didchange            [self refreshTextViewSize:textView];            //既然是超出部分截取了,哪一定是最大限制了。            self.lbNums.text = [NSString stringWithFormat:@"%d/%ld",0,(long)MAX_LIMIT_NUMS];        }        return NO;    }}- (void)textViewDidChange:(UITextView *)textView{    UITextRange *selectedRange = [textView markedTextRange];    //擷取高亮部分    UITextPosition *pos = [textView positionFromPosition:selectedRange.start offset:0];        //如果在變化中是高亮部分在變,就不要計算字元了    if (selectedRange && pos) {        return;    }        NSString  *nsTextContent = textView.text;    NSInteger existTextNum = nsTextContent.length;        if (existTextNum > MAX_LIMIT_NUMS)    {        //截取到最大位置的字元(由於超出截部分在should時被處理了所在這裡這了提高效率不再判斷)        NSString *s = [nsTextContent substringToIndex:MAX_LIMIT_NUMS];                [textView setText:s];    }        //不讓顯示負數 口口日    self.lbNums.text = [NSString stringWithFormat:@"%ld/%d",MAX(0,MAX_LIMIT_NUMS - existTextNum),MAX_LIMIT_NUMS];        [self refreshTextViewSize:textView];}

好了。大功告成。也為自己做的筆記吧,每次頁面上要處理限制,總要寫一次,裡面涉及的細節也比較多。因此直接貼代碼。下次好直接COPY來用。呵呵。copy是個不好的習慣。。。。。。。若用須謹慎。謝謝大家。




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.