ほんとに検索ひっかからないこれガチャピン
すっとできたと思ったら、意外と苦労した、、でも絵文字対応してません。
いつかねにこ

/**

 * 最後に『...続きを読む』をつける

 */

+ (void)seeMoreTruncatingTail:(UITextView *)textView numberOfLines:(int)numberOfLines

{

    // 省略文字

    NSString *seeMoreString = [NSString stringWithFormat:@" ...%@",NSLocalizedString(@"続きを読む"@"See More")];


    // オリジナルサイズ

    textView.textContainer.lineBreakMode = NSLineBreakByCharWrapping;

    

    // はかりなおしがうまくいかないときがあるので使わない

//    textView.textContainer.maximumNumberOfLines = numberOfLines;

//    CGSize originalSize = [textView sizeThatFits:textView.frame.size];

    CGSize originalSize = textView.frame.size;


    // フルサイズ

    textView.textContainer.maximumNumberOfLines = 0;

    textView.text = textView.text// 入れなおさないとうまくいかない時がある

    CGSize fullSize = [textView sizeThatFits:CGSizeMake((CGFloat)textView.width,CGFLOAT_MAX)];


    // 省略するかどうか

    CGFloat restHeight  = originalSize.height;

    NSString *text      = textView.text;

    NSString *inputText = @"";

    NSString *line      = textView.text;

// 心配だったらこっちもいれとく    

    if (numberOfLines == 3) {

        restHeight = 17.5; // ※サンプル数字

        

    } else if (numberOfLines == 2) {

        restHeight = 14.5; // ※サンプル数字

    }

    

    if (fullSize.height > originalSize.height + SPACE_S) {

        

        // 改行があるとき

        NSRange searchResult = [textView.text rangeOfString:@"\n"];

        if (searchResult.location != NSNotFound) {

            NSRange range, subRange;

            

            // 最初に文字列全範囲を示すRangeを作成する

            range = NSMakeRange(0, text.length);

            

            // 1行ずつ読み出す

            while (range.length > 0) {

                // 1行分を示すRangeを取得

                subRange = [text lineRangeForRange:NSMakeRange(range.location,0)];

                // 1行分を示すRangeを用いて、文字列から1行抜き出す

                line = [text substringWithRange:subRange];

                range.location = NSMaxRange(subRange);

                range.length -= subRange.length;

                

                textView.text = [line stringByReplacingOccurrencesOfString:@"\n"withString:@""];

                CGSize lineSize = [textViewsizeThatFits:CGSizeMake((CGFloat)textView.widthCGFLOAT_MAX)];


                // この文で最大行を超えているか

                if (restHeight - lineSize.height < SPACE_S) {

                    break;

                }

                inputText = [NSString stringWithFormat:@"%@%@", inputText, line];

                restHeight = restHeight - lineSize.height;

            }

        }

        

        // 最後が改行だけでなければ改行をぬく

        int plusCount = 0;

        BOOL isNoCut  = NO;

        NSString *originalLine = line;

        if ([line isEqualToString:@"\n"] == NO) {

            line = [line stringByReplacingOccurrencesOfString:@"\n" withString:@""];

            if ([line isEqualToString:originalLine] == NO) {

                plusCount++;

                

                // 最後の改行を抜いて省略文字をいれてみよう

                textView.text = [NSString stringWithFormat:@"%@%@", line, seeMoreString];

                CGSize lineSize = [textViewsizeThatFits:CGSizeMake((CGFloat)textView.widthCGFLOAT_MAX)];

                if (restHeight > lineSize.height) {

                    isNoCut = YES;

                }

            }

        }

        

        // 表示文字数を取得

        NSAttributedString *attrString = [[NSAttributedString alloc]initWithString:line

                                                                        attributes:@{NSFontAttributeName:textView.font}];

        

        CTFramesetterRef framesetter =CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);

        CGMutablePathRef path = CGPathCreateMutable();

        CGPathAddRect(path, NULLCGRectMake(00, textView.width, restHeight));

        

        CTFrameRef frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(00), path, NULL);

        CFRange actuallyRenderedRange = CTFrameGetVisibleStringRange(frame);

        // 省略文字のバイト数分を引く

        NSString *actuallyRenderedText = line;

        

        if (isNoCut == NO

            && (int)(actuallyRenderedRange.length > (int)[seeMoreStringlengthOfBytesUsingEncoding:NSShiftJISStringEncoding])) {

            

            // 省略部分がすべて全角なら文字数分引く

            NSString *delString = [linesubstringWithRange:NSMakeRange((int)actuallyRenderedRange.length - (int)seeMoreString.length, (int)seeMoreString.length)];


            if ((int)delString.length*2 == (int)[delStringlengthOfBytesUsingEncoding:NSShiftJISStringEncoding]) {

                actuallyRenderedText = [line substringWithRange:NSMakeRange(0, (int)((int)actuallyRenderedRange.length - (int)seeMoreString.length + plusCount))];

                

            // それ以外はバイト数分

            } else {

                actuallyRenderedText = [line substringWithRange:NSMakeRange(0, (int)((int)actuallyRenderedRange.length - (int)[seeMoreStringlengthOfBytesUsingEncoding:NSShiftJISStringEncoding] + plusCount))];

            }

        }


        // リリース

        CGPathRelease(path);

        CFRelease(framesetter);

        CFRelease(frame);


        // 省略文字をつけてセット

        NSMutableAttributedString *attrMutableString = [[NSMutableAttributedString allocinit];

        NSAttributedString *string1 = [[NSAttributedString allocinitWithString:

                                       [NSString stringWithFormat:@"%@%@", inputText, actuallyRenderedText]

                                                                     attributes:@{NSFontAttributeName:textView.font,

                                                                                  NSForegroundColorAttributeName:textView.textColor}];

        NSAttributedString *string2 = [[NSAttributedString alloc]initWithString:seeMoreString

                                                                     attributes:@{NSFontAttributeName:[UIColor blackColor],

                                                                                  NSForegroundColorAttributeName:[UIColor grayColor]}];

        [attrMutableString appendAttributedString:string1];

        [attrMutableString appendAttributedString:string2];

        

        textView.textContainer.maximumNumberOfLines = numberOfLines;

        textView.attributedText = attrMutableString;

    }

    return;

}


参考まで
テヘッ