如何从UILabel的第n行获取text/String?

iph*_*aaw 14 uilabel core-text ios swift

有没有一种简单的方法来获取(或简单地显示)UILabel中给定行的文本?

我的UILabel正确地显示我的文本并将其精美地展示出来,但偶尔我需要能够显示某些行,但显然我需要知道UILabel如何定位所有这些.

我知道这可以通过子字符串轻松完成,但我需要知道该行的起点和终点.

或者,如果UILabel的框架有某种偏移并且隐藏了我不想看到的其他内容,我可以滚动UILabel.

我无法发现任何显示如何轻松完成此操作的内容.有人有什么好主意吗?

谢谢

iphaaw

The*_*ger 23

我有更好的方法来找到它.

您可以在CoreText.framework的帮助下获得此功能.

1.添加CoreText.framework.
2.进口#import <CoreText/CoreText.h>.
然后使用以下方法:

- (NSArray *)getLinesArrayOfStringInLabel:(UILabel *)label {
    NSString *text = [label text];
    UIFont   *font = [label font];
    CGRect    rect = [label frame];

    CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);
    NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
    [attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];


    CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge 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 = (__bridge NSArray *)CTFrameGetLines(frame);
    NSMutableArray *linesArray = [[NSMutableArray alloc]init];

    for (id line in lines)
    {
        CTLineRef lineRef = (__bridge CTLineRef )line;
        CFRange lineRange = CTLineGetStringRange(lineRef);
        NSRange range = NSMakeRange(lineRange.location, lineRange.length);

        NSString *lineString = [text substringWithRange:range];
        [linesArray addObject:lineString];
    }

    return (NSArray *)linesArray;
}
Run Code Online (Sandbox Code Playgroud)

调用此方法: -

NSArray *linesArray = [self getLinesArrayOfStringInLabel:yourLabel];
Run Code Online (Sandbox Code Playgroud)

现在你可以使用了linesArray.

SWIFT 4版本

func getLinesArrayOfString(in label: UILabel) -> [String] {

        /// An empty string's array
        var linesArray = [String]()

        guard let text = label.text, let font = label.font else {return linesArray}

        let rect = label.frame

        let myFont: CTFont = CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil)
        let attStr = NSMutableAttributedString(string: text)
        attStr.addAttribute(kCTFontAttributeName as NSAttributedString.Key, value: myFont, range: NSRange(location: 0, length: attStr.length))

        let frameSetter: CTFramesetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
        let path: CGMutablePath = CGMutablePath()
        path.addRect(CGRect(x: 0, y: 0, width: rect.size.width, height: 100000), transform: .identity)

        let frame: CTFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
        guard let lines = CTFrameGetLines(frame) as? [Any] else {return linesArray}

        for line in lines {
            let lineRef = line as! CTLine
            let lineRange: CFRange = CTLineGetStringRange(lineRef)
            let range = NSRange(location: lineRange.location, length: lineRange.length)
            let lineString: String = (text as NSString).substring(with: range)
            linesArray.append(lineString)
        }
        return linesArray
 }
Run Code Online (Sandbox Code Playgroud)

使用:

let lines: [String] = getLinesArrayOfString(in: label)
Run Code Online (Sandbox Code Playgroud)


Nar*_*hal 8

斯威夫特3

func getLinesArrayFromLabel(label:UILabel) -> [String] {

        let text:NSString = label.text! as NSString // TODO: Make safe?
        let font:UIFont = label.font
        let rect:CGRect = label.frame

        let myFont:CTFont = CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil)
        let attStr:NSMutableAttributedString = NSMutableAttributedString(string: text as String)
        attStr.addAttribute(String(kCTFontAttributeName), value:myFont, range: NSMakeRange(0, attStr.length))
        let frameSetter:CTFramesetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
        let path:CGMutablePath = CGMutablePath()
        path.addRect(CGRect(x:0, y:0, width:rect.size.width, height:100000))

        let frame:CTFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
        let lines = CTFrameGetLines(frame) as NSArray
        var linesArray = [String]()

        for line in lines {
            let lineRange = CTLineGetStringRange(line as! CTLine)
            let range:NSRange = NSMakeRange(lineRange.location, lineRange.length)
            let lineString = text.substring(with: range)
            linesArray.append(lineString as String)
        }
        return linesArray
}
Run Code Online (Sandbox Code Playgroud)

Swift 2(Xcode 7)版本(测试,并从Swift 1回答重新编辑)

func getLinesArrayOfStringInLabel(label:UILabel) -> [String] {

let text:NSString = label.text! // TODO: Make safe?
let font:UIFont = label.font
let rect:CGRect = label.frame

let myFont:CTFontRef = CTFontCreateWithName(font.fontName, font.pointSize, nil)
let attStr:NSMutableAttributedString = NSMutableAttributedString(string: text as String)
attStr.addAttribute(String(kCTFontAttributeName), value:myFont, range: NSMakeRange(0, attStr.length))
let frameSetter:CTFramesetterRef = CTFramesetterCreateWithAttributedString(attStr as CFAttributedStringRef)
let path:CGMutablePathRef = CGPathCreateMutable()
CGPathAddRect(path, nil, CGRectMake(0, 0, rect.size.width, 100000))
let frame:CTFrameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
let lines = CTFrameGetLines(frame) as NSArray
var linesArray = [String]()

for line in lines {
    let lineRange = CTLineGetStringRange(line as! CTLine)
    let range:NSRange = NSMakeRange(lineRange.location, lineRange.length)
    let lineString = text.substringWithRange(range)
    linesArray.append(lineString as String)
}
return linesArray
}
Run Code Online (Sandbox Code Playgroud)

  • 起初它对我不起作用 - 计算的行数超过实际数.但后来我将CGPathAddRect的行更改为:CGPathAddRect(path,nil,CGRectMake(0,0,rect.size.width + 15,100000))并且它按预期工作. (4认同)

小智 5

回答正确发布!!!!

-(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;
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*oda 5

关于 iOS 11+ 的非常重要的变化

从 iOS 11 开始,Apple 有意更改了其自动换行功能的行为,该功能UILabel检测Stringmultiline 中单个行的内容UILabel。按照设计,UILabelnow 的自动换行避免了孤立文本(新行中的单个单词),如下所述:iOS 11 中的自动换行

因此,如果避免孤立文本的新自动换行在特定行中生效,则CTFrameGetLines(frame)返回CTLine标签中所有行的数组的方法将不再正常工作。相反,它导致部分String由新的自动换行设计将属于下一行而不是在焦点行中结束。

在我修改过的@TheTiger's answer版本中可以找到针对此问题的经过测试的修复程序,它利用计算UILabelusing的实际内容大小sizeThatFits(size:),然后使用该大小创建以Swift 4编写的 rect / 路径:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这个UILabel扩展将标签的内容作为一个String数组返回,每行一个条目,与呈现给用户的眼睛完全一样。


pas*_*ine 4

我认为没有一种本地方法可以做到这一点(例如“takethenline”方法)。
我可以找到一个棘手的解决方案,但我不确定这是最好的解决方案。
您可以将标签拆分为单词数组。
然后您可以循环数组并检查文本高度,直到该单词如下所示:

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++;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果高度发生变化,则意味着单词之前有换行符。
我现在无法检查语法是否写得正确,所以你必须自己检查。