iOS + 根据UITextView获取任一字符位置、末尾字符位置

需求:设置标题,最多显示两行,多余的字符串以"..."截断,并在标题末尾追加编辑按钮

  1. 获取UITextView中任一字符位置

     - (CGRect)rectOfCharacterAtIndex:(NSInteger)index inTextView:(UITextView *)textView {
         if (!textView) {
             return CGRectZero;
         }
        
         NSString *text = textView.text;
         if (index <= 0 && index >= text.length) {
             return CGRectZero;
         }
         
         // 获取每个字符的位置
         [textView setSelectedRange:NSMakeRange(index,1)];
         NSArray<UITextSelectionRect *> *rects = [textView selectionRectsForRange:textView.selectedTextRange];
         [textView setSelectedRange:NSMakeRange(0, 0)];
         
         // 获取字符有效的位置
         CGRect charRect = rects.firstObject.rect;
         for (UITextSelectionRect *rect in rects) {
             // rect有效的情况下
             if (rect.rect.size.width > 0 && rect.rect.size.height > 0) {
                 if (CGRectGetMinY(charRect) <  CGRectGetMinY(rect.rect)) {
                     charRect = rect.rect;
                 } else if (CGRectGetMinY(charRect) == CGRectGetMinY(rect.rect) && CGRectGetMinX(charRect) < CGRectGetMinX(rect.rect)) {
                     charRect = rect.rect;
                 }
             }
         }
    
         return charRect;
     }
    
  2. 获取每一行字符串的长度及该行最后一个字符下标

     // 计算每一行的rect及最后一个字符的下标
     - (NSArray *)lineInformaitonOfTextView:(UITextView *)textView {
         NSString *text = textView.text;
         NSMutableDictionary<NSString *, NSDictionary *> *lineInfo = [NSMutableDictionary dictionary];
         for (NSInteger index = 0; index < text.length; index++) {
             CGRect charRect = [self rectOfCharacterAtIndex:index inTextView:textView];
             NSString *YKey = [NSString stringWithFormat:@"%.0f", charRect.origin.y];
             NSMutableDictionary *info = [lineInfo[YKey] mutableCopy];
             if (!info) {
                 info = [NSMutableDictionary dictionary];
             }
             
             info[@"lastCharIndex"] = @(index);
             
             if (!info[@"rect"]) {
                 info[@"rect"] = [NSValue valueWithCGRect:charRect];
             } else {
                 CGRect rect = [info[@"rect"] CGRectValue];
                 if (rect.origin.x > charRect.origin.x) {
                     rect.origin.x = charRect.origin.x;
                 }
                 if (rect.origin.y > charRect.origin.y) {
                     rect.origin.y = charRect.origin.y; 
                 }
                 if (CGRectGetMaxX(rect) < CGRectGetMaxX(charRect)) {
                     rect.size.width = CGRectGetMaxX(charRect) - CGRectGetMinX(rect);
                 }
                 if (CGRectGetMaxY(rect) < CGRectGetMaxY(charRect)) {
                     rect.size.height = CGRectGetMaxY(charRect) - CGRectGetMinY(rect); 
                 }
                 info[@"rect"] = [NSValue valueWithCGRect:rect];
             }
             lineInfo[YKey] = info;
         }
         
         // 对行排序
         NSMutableArray *allYValue = [lineInfo.allKeys mutableCopy];
         [allYValue sortUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
             if (obj1.integerValue <= obj2.integerValue) {
                 return NSOrderedAscending;
             } else {
                 return NSOrderedDescending;
             }
         }];
         
         // 按行顺序返回数据
         NSMutableArray *lineArray = [NSMutableArray array];
         for (NSString *lineKey in allYValue) {
             [lineArray addObject:lineInfo[lineKey]];
         }
         return lineArray;
     }
    
  3. 限制行数、及限制最后一行的长度

     /** 限制行数且以...在末尾截断,最后一行末尾空出间隔追加控件
      * @param numberOfLine 限制行数
      * @param appendLength 最后一行的末尾空白区
     */
     - (void)setString:(NSString *)string attributes:(NSDictionary *)attributes textView:(UITextView *)textView maxNumberOfLine:(NSInteger)numberOfLine appendLength:(CGFloat)appendLength {
         if (!textView) {
             return ;
         }
         
         // 设置字符串
         string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
         textView.text = string;
         if (attributes && string) {
             textView.text = nil;
             NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:string attributes:attributes];
             textView.attributedText = attributeString;
         }
         CGRect textFrame = textView.frame;
         textFrame.size.height = [textView sizeThatFits:CGSizeMake(textFrame.size.width, MAXFLOAT)].height;
         textView.frame = textFrame;
         
         // 获取每一行字符串位置
         NSArray *lineInfoArray = [self lineInformaitonOfTextView:textView];
         
         // 当行号等于或超过限制行数时,需调整最后一行的字符,以便能空出appendLength的位置
         if (lineInfoArray.count >= numberOfLine) {
             NSDictionary *lineInfo = lineInfoArray[numberOfLine - 1];
             CGFloat adjustLastLineLength = CGRectGetMaxX([lineInfo[@"rect"] CGRectValue]); // 调整过后,最后一行字符的宽度
             NSInteger adjustIndex = [lineInfo[@"lastCharIndex"] integerValue]; // 调整过后,字符开始截断的位置
             // 最后一行的空余位置不够appendLength时,才需要偏移
             if (adjustIndex > 0 && adjustLastLineLength + appendLength > textFrame.size.width) {
                 // 偏移下标,空出appendLength的位置
                 for (; adjustIndex > 0; adjustIndex--) {
                     CGRect charRect = [self rectOfCharacterAtIndex:adjustIndex inTextView:textView];
                     if (CGRectGetMinX(charRect) + appendLength < textFrame.size.width) {
                         break;
                     }
                 }
                 
                 // 截断字符串
                 NSString *adjustText = textView.text;
                 if (adjustIndex >= 0 && adjustIndex < adjustText.length) {
                     adjustText = [adjustText stringByReplacingCharactersInRange:NSMakeRange(adjustIndex, adjustText.length - adjustIndex) withString:@"..."];
                     // 重新设置值
                     textView.text = adjustText;
                     if (attributes && adjustText) {
                         textView.text = nil;
                         NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:adjustText attributes:attributes];
                         textView.attributedText = attributeString;
                     }
                 }
             }
         }
         
         textFrame.size.height = [textView sizeThatFits:CGSizeMake(textFrame.size.width, MAXFLOAT)].height;
         textView.frame = textFrame;
     }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容