How to get text / String from nth line of UILabel?

前端 未结 10 1627
孤街浪徒
孤街浪徒 2020-11-27 05:27

Is there an easy way to get (or simply display) the text from a given line in a UILabel?

My UILabel is correctly displaying my text and laying it out beautifully bu

相关标签:
10条回答
  • 2020-11-27 05:42

    I don't think there's a native way for doing this (like a "takethenline" method).
    I can figure out a tricky solution but I'm not sure is the best one.
    You could split your label into an array of words.
    Then you could loop the array and check the text height until that word like this:

    NSString *texttocheck;
    float old_height = 0;
    int linenumber = 0; 
    
    for (x=0; x<[wordarray lenght]; x++) {
        texttocheck = [NSString stringWithFormat:@"%@ %@", texttocheck, [wordarray objectAtIndex:x]];
    
        float height = [text sizeWithFont:textLabel.font
                        constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
                            lineBreakMode:UILineBreakModeWordWrap].height;
    
        if (old_height < height) {
            linenumber++;
        }
    }
    

    If height changes, it means there's a line break before the word.
    I can't check if the syntax is written correctly now, so you have to check it yourself.

    0 讨论(0)
  • 2020-11-27 05:44

    Answer with Proper release !!!!

    -(NSArray *)getLinesArrayOfStringInLabel:(UILabel *)label
    {
        NSString *text = [label text];
        UIFont   *font = [label font];
        CGRect    rect = [label frame];
    
        CTFontRef myFont = CTFontCreateWithName(( CFStringRef)([font fontName]), [font pointSize], NULL);
        NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
        [attStr addAttribute:(NSString *)kCTFontAttributeName value:( id)myFont range:NSMakeRange(0, attStr.length)];
    
        CFRelease(myFont);
    
        CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(( CFAttributedStringRef)attStr);
    
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, CGRectMake(0,0,rect.size.width,100000));
    
        CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
    
        NSArray *lines = ( NSArray *)CTFrameGetLines(frame);
    
        NSMutableArray *linesArray = [[NSMutableArray alloc]init];
    
        for (id line in lines)
        {
            CTLineRef lineRef = ( CTLineRef )line;
            CFRange lineRange = CTLineGetStringRange(lineRef);
            NSRange range = NSMakeRange(lineRange.location, lineRange.length);
    
            NSString *lineString = [text substringWithRange:range];
    
            CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr, lineRange, kCTKernAttributeName, (CFTypeRef)([NSNumber numberWithFloat:0.0]));
            CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr, lineRange, kCTKernAttributeName, (CFTypeRef)([NSNumber numberWithInt:0.0]));
    
            //NSLog(@"''''''''''''''''''%@",lineString);
            [linesArray addObject:lineString];
    
        }
        [attStr release];
    
        CGPathRelease(path);
        CFRelease( frame );
        CFRelease(frameSetter);
    
    
        return (NSArray *)linesArray;
    }
    
    0 讨论(0)
  • 2020-11-27 05:49

    If all your characters are displayed in the same size, i.e. they're enclosed in a box of common size, you can exploit that. (This seems to be the case with Japanese characters, for example.)

    Otherwise you can query the size of each character in the display font and calculate what the line would have to be.

    The only worry then is that your calculation might disagree with what Apple's doing behind the scenes - in which case, I recommend you go to the trouble of overriding the text frame drawing. Look up Core Text in the documents for this.

    (I may have been doing this wrong, but I didn't find Apple's method as given in the docs was very accurate, so I did something else myself.)

    0 讨论(0)
  • 2020-11-27 05:50

    Very important change regarding iOS 11+

    Starting with iOS 11, Apple intentionally changed the behaviour of their word-wrapping feature for UILabel which effects detecting the String contents of individual lines in a multiline UILabel. By design, the word-wrapping of the UILabel now avoids orphaned text (single words in a new line), as discussed here: word wrapping in iOS 11

    Because of that, the way CTFrameGetLines(frame) returns the CTLine array of all lines in the label no longer works correctly if the new word-wrapping that avoids orphaned text takes effect in a particular line. To the contrary, it results in parts of the String that by the new word wrapping design would belong to the next line instead end up in the line in focus.

    A tested fix for this problem can be found in my altered version of @TheTiger's answer, which makes use of calculating the actual content size of the UILabel using sizeThatFits(size:), before using that size to create the rect / path written in Swift 4:

    extension UILabel {
    
        /// creates an array containing one entry for each line of text the label has
        var lines: [String]? {
    
            guard let text = text, let font = font else { return nil }
    
            let attStr = NSMutableAttributedString(string: text)
            attStr.addAttribute(NSAttributedString.Key.font, value: font, range: NSRange(location: 0, length: attStr.length))
    
            let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
            let path = CGMutablePath()
    
            // size needs to be adjusted, because frame might change because of intelligent word wrapping of iOS
            let size = sizeThatFits(CGSize(width: self.frame.width, height: .greatestFiniteMagnitude))
            path.addRect(CGRect(x: 0, y: 0, width: size.width, height: size.height), transform: .identity)
    
            let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path, nil)
            guard let lines = CTFrameGetLines(frame) as? [Any] else { return nil }
    
            var linesArray: [String] = []
    
            for line in lines {
                let lineRef = line as! CTLine
                let lineRange = CTLineGetStringRange(lineRef)
                let range = NSRange(location: lineRange.location, length: lineRange.length)
                let lineString = (text as NSString).substring(with: range)
                linesArray.append(lineString)
            }
            return linesArray
        }
    }
    

    This UILabel extension returns the contents of the label as a String array with one entry per line exactly as presented to the eye of the user.

    0 讨论(0)
提交回复
热议问题