问题 从更长的NSString返回一个包含字的NSString [重复]


可能重复:
UITextView:获取包装信息的文本 

我一直在寻找NSString库和许多库的函数,可以采用这样的长字符串:

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.

并与CGSize或浮点一起指示宽度和正在使用的字体,并返回一个字符串,其中包含\ n符号和单词包装。

结果(大致):

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac\n
egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet.\n
 Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. \n
placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra.\n
 Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi.\n
 Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci,\n
 sagittis tempus lacus enim ac dui. 

我已经知道UITextViews这样做了,但是这没有用,因为我需要在原始的OpenGL环境中渲染文本,所以我没有使用常规的UI元素。

我知道这既存在于框架中,也存在于某个公共类中。我只是简单地找不到任何统一的处理方法。

我想它接近[NSString sizeWithFont:forWidth:lineBreakMode:],但我不需要大小,我需要字符串本身。


4806
2017-11-27 13:19


起源

猜猜你必须使用你已经提到的sizeWithFont调用自己写一些东西。 - jimpic
@cem,即使问题不一样,解决方案足够接近我可以提供一个功能来解决我的问题。 - Nils Munch
你用什么API来绘制字符串?如果你已经将每个角色渲染为纹理(对于许多非ASCII文本可能会破坏很多),那么只需使用那里测量的宽度; UIKit的字符串绘图使用“分数”宽度,这可能不是你想要的。或者,使用UIKit绘制到位图,然后将其移动到OpenGL中(但请注意,glDrawPixels()在某些情况下可能会很慢)。另请注意UIKit字符串绘图(也可能还有 -sizeWithFont:...)是 不 iOS 5.x中的线程安全: stackoverflow.com/questions/11589768/... - tc.


答案:


确实没有必要重新发明这个轮子,因为它正是文本引擎每次包装文本时为你做的。什么  文本引擎?它是核心文本。如果您下拉到Core Text的级别并让CTFramesetter为您布置文本,您可以通过询问生成的CTLines来了解换行符的位置。

该文档将帮助您入门:

http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/CoreText_Programming/Operations/Operations.html

网上有很多很好的教程。

简单的例子:

NSString* s = @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
@"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
@"enim ad minim veniam, quis nostrud exercitation ullamco laboris "
@"nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor "
@"in reprehenderit in voluptate velit esse cillum dolore eu fugiat "
@"nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
@"sunt in culpa qui officia deserunt mollit.";
NSAttributedString* text = [[NSAttributedString alloc] initWithString:s];

CTFramesetterRef fs =
CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)text);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,200,100000));
CTFrameRef f = CTFramesetterCreateFrame(fs, CFRangeMake(0, 0), path, NULL);
CTFrameDraw(f, NULL);

NSArray* lines = (__bridge NSArray*)CTFrameGetLines(f);
for (id aLine in lines) {
    CTLineRef theLine = (__bridge CTLineRef)aLine;
    CFRange range = CTLineGetStringRange(theLine);
    NSLog(@"%ld %ld", range.location, range.length);
}
CGPathRelease(path);
CFRelease(f);
CFRelease(fs);

正如您将看到的,输出显示了每行包装文本的范围。这不是你想要的那种东西吗?


9
2017-11-27 16:10



如果这是一个愚蠢的问题请原谅我,因为我没有机会研究它,但是现在iOS 7有NSLayoutManager和NSTextStorage,我们可以做同样的事情而不直接处理CoreText框架吗?例如。通过使用其中一些更高级别的框架来代替? - Senseful
这根本不是一个愚蠢的问题,是的,我们可以!而且它更容易。布局管理器很乐意告诉您每个线段的完整字形/文本。 - matt
太棒了,谢谢你的信息。我把它作为一个单独的问题发布: 如何使用新的iOS 7 API获取自动换行信息? - Senseful


似乎没有工厂方式这样做,所以已经开始构建一个类函数来处理这个,基于引入的解决方案 这个密切相关的堆栈 :

+ (NSString*)wrappedString:(NSString*)string withFont:(UIFont*)font andWidth:(float)width {
    NSMutableString *resultString = [[NSMutableString alloc] initWithString:@""];

    CGSize textSize = [string sizeWithFont:font];
    float textWidth = textSize.width;
    if (textWidth < width) {
        return string;
    }
    float wordLength;
    float lineLength;
    NSUInteger length = [string length];
    unichar buffer[length];
    [string getCharacters:buffer range:NSMakeRange(0, length)];

    NSString *singleLine = @"";
    NSString *word = @"";
    NSString *longWord = @"";

    for (NSUInteger i = 0; i < length; i++) {

        unichar character = buffer[i];
        if (character != '\n') {
            word = [NSString stringWithFormat:@"%@%c", word, character];
        }

        if (character == '\n') {
            float wordLength = [word sizeWithFont:font].width;
            float lineLength = [singleLine sizeWithFont:font].width;
            if ((lineLength + wordLength) > width) {
                [resultString appendString:singleLine];
                [resultString appendString:@"\n"];
                singleLine = @"";
                singleLine = [singleLine stringByAppendingFormat:@"%@\n",word];
                word = @"";
            } else {
                singleLine = [singleLine stringByAppendingString: word];
                word = @"";
                [resultString appendString:singleLine];
                [resultString appendString:@"\n"];
                singleLine = @"";
            }
        }

        else if (character == ' ') {
            float wordLength = [word sizeWithFont:font].width;
            float lineLength = [singleLine sizeWithFont:font].width;

            if ((lineLength + wordLength) > width) {
                if (wordLength > textWidth) {
                    [resultString appendString:singleLine];
                    [resultString appendString:@"\n"];
                    singleLine = @"";
                    int j = 0;
                    for (; j < [word length]; j++) {
                        unichar longChar = [word characterAtIndex:j];
                        longWord = [NSString stringWithFormat:@"%@%c", longWord, longChar];
                        float longwordLength = [longWord sizeWithFont:font].width;
                        float longlineLength = [singleLine sizeWithFont:font].width;
                        if ((longlineLength + longwordLength) >= width) {
                            singleLine = [singleLine stringByAppendingString:longWord];
                            word = @"";
                            longWord = @"";
                            break;
                        }
                    }

                }
                [resultString appendString:singleLine];
                [resultString appendString:@"\n"];
                singleLine = @"";
            }
            singleLine = [singleLine stringByAppendingString: word];
            word = @"";
        }
    }

    wordLength = [word sizeWithFont:font].width;
    lineLength = [singleLine sizeWithFont:font].width;

    if (wordLength > 0) {
        if ((lineLength + wordLength) > width) {
            [resultString appendString:singleLine];
            [resultString appendString:@"\n"];
            singleLine = @"";
        }
        singleLine = [singleLine stringByAppendingString:word];
    }


    if (lineLength > 0) {
        [resultString appendString:singleLine];
        [resultString appendString:@"\n"];
    }
    return resultString;
}

1
2017-11-27 14:12



[NSString stringWithFormat:@""]; 大声笑 - trojanfoe
仍然从原来的木马上抛光这个...... - Nils Munch