UILabel检测换行符

时间:2013-03-05 22:57:04

标签: nsstring uilabel line-breaks

我目前正在使用UILabel来显示多行文字。换行符最多设置为NSLineBreakByWordWrapping,因此标签会自动插入换行符。如何检测这些换行符的位置?基本上,我想返回包含每个\n中断的字符串。

2 个答案:

答案 0 :(得分:3)

以下是我在UILabel

中检测换行符的工作
- (int)getLengthForString:(NSString *)str fromFrame:(CGRect)frame withFont:(UIFont *)font
{
    int length = 1;
    int lastSpace = 1;
    NSString *cutText = [str substringToIndex:length];
    CGSize textSize = [cutText sizeWithFont:font constrainedToSize:CGSizeMake(frame.size.width, frame.size.height + 500)];
    while (textSize.height <= frame.size.height)
    {
        NSRange range = NSMakeRange (length, 1);
        if ([[str substringWithRange:range] isEqualToString:@" "])
        {
            lastSpace = length;
        }
        length++;
        cutText = [str substringToIndex:length];
        textSize = [cutText sizeWithFont:font constrainedToSize:CGSizeMake(frame.size.width, frame.size.height + 500)];
    }
    return lastSpace;
}


-(NSString*) getLinebreaksWithString:(NSString*)labelText forWidth:(CGFloat)lblWidth forPoint:(CGPoint)point
{
    //Create Label
    UILabel *label = [[UILabel alloc] init];
    label.text = labelText;
    label.numberOfLines = 0;
    label.lineBreakMode = NSLineBreakByWordWrapping;

    //Set frame according to string
    CGSize size = [label.text sizeWithFont:label.font
                         constrainedToSize:CGSizeMake(lblWidth, MAXFLOAT)
                             lineBreakMode:UILineBreakModeWordWrap];
    [label setFrame:CGRectMake(point.x , point.y , size.width , size.height)];

    //Add Label in current view
    [self.view addSubview:label];

    //Count the total number of lines for the Label which has NSLineBreakByWordWrapping line break mode
    int numLines = (int)(label.frame.size.height/label.font.leading);

    //Getting and dumping lines from text set on the Label
    NSMutableArray *allLines = [[NSMutableArray alloc] init];

    BOOL shouldLoop = YES;
    while (shouldLoop)
    {
        //Getting length of the string from rect with font
        int length = [self getLengthForString:labelText fromFrame:CGRectMake(point.x, point.y, size.width, size.height/numLines) withFont:label.font] + 1;        

        [allLines addObject:[labelText substringToIndex:length]];

        labelText = [labelText substringFromIndex:length];
        if(labelText.length<length)
            shouldLoop = NO;
    }

    [allLines addObject:labelText];
    //NSLog(@"\n\n%@\n",allLines);

    return [allLines componentsJoinedByString:@"\n"];
}

如何使用

NSString *labelText = @"We are what our thoughts have made us; so take care about what you think. Words are secondary. Thoughts live; they travel far.";

NSString *stringWithLineBreakChar = [self getLinebreaksWithString:labelText forWidth:200 forPoint:CGPointMake(15, 15)];
NSLog(@"\n\n%@",stringWithLineBreakChar);

你只需要设置 getLinebreaksWithString 所需的参数,你就会得到一个字符串,每个&#34; \ n&#34;包括在内。

<强>输出 Result of NSLog and Simulator

答案 1 :(得分:0)

这是我解决这个问题的方法 我遍历提供的字符串中的所有空白字符,将子字符串(从开头到当前空白)设置为我的标签,并检查高度的差异。当高度改变时,我插入一个换行符(模拟换行符)。

func wordWrapFormattedStringFor(string: String) -> String {
    // Save label state
    let labelHidden = label.isHidden
    let labelText = label.text

    // Set text to current label and size it to fit the content
    label.isHidden = true
    label.text = string
    label.sizeToFit()
    label.layoutIfNeeded()

    // Prepare array with indexes of all whitespace characters
    let whitespaceIndexes: [String.Index] = {
        let whitespacesSet = CharacterSet.whitespacesAndNewlines
        var indexes: [String.Index] = []

        string.unicodeScalars.enumerated().forEach { i, unicodeScalar in
            if whitespacesSet.contains(unicodeScalar) {
                let index = string.index(string.startIndex, offsetBy: i)
                indexes.append(index)
            }
        }

        // We want also the index after the last character so that when we make substrings later we include also the last word
        // This index can only be used for substring to this index (not from or including, since it points to \0)
        let index = string.index(string.startIndex, offsetBy: string.characters.count)
        indexes.append(index)

        return indexes
    }()

    var reformattedString = ""

    let boundingSize = CGSize(width: label.bounds.width, height: CGFloat.greatestFiniteMagnitude)
    let attributes = [NSFontAttributeName: font]

    var runningHeight: CGFloat?
    var previousIndex: String.Index?

    whitespaceIndexes.forEach { index in
        let string = string.substring(to: index)
        let stringHeight = string.boundingRect(with: boundingSize, options: .usesLineFragmentOrigin, attributes: attributes, context: nil).height

        var newString: String = {
            if let previousIndex = previousIndex {
                return string.substring(from: previousIndex)
            } else {
                return string
            }
        }()

        if runningHeight == nil {
            runningHeight = stringHeight
        }

        if runningHeight! < stringHeight {
            // Check that we can create a new index with offset of 1 and that newString does not contain only a whitespace (for example if there are multiple consecutive whitespaces)
            if newString.characters.count > 1 {
                let splitIndex = newString.index(newString.startIndex, offsetBy: 1)
                // Insert a new line between the whitespace and rest of the string
                newString.insert("\n", at: splitIndex)
            }
        }

        reformattedString += newString

        runningHeight = stringHeight
        previousIndex = index
    }

    // Restore label
    label.text = labelText
    label.isHidden = labelHidden

    return reformattedString
}

Image - Comparison of output string with label text.

这个解决方案在我的案例中运作得非常好,希望它有所帮助。