Ankit Kumar Gupta
Ankit Kumar Gupta

Reputation: 4042

Get line break in UILabel due to Autolayout

I want to get the text with newline characters as the UILabel is displayed using autolayout on the phone's screen. Thanks.

For example : If I assign

label.text = "dhasghdsgah dgahjdg ahjgd hkagkhdhk ajsh djkah"

When this is displayed on screen it appears as follows depending on screen width:

dhasghdsgah dgahjdg ahjgd

 hkagkhdhk ajsh

  djkah

I want to know where the '\n' has been inserted by autolayout.

Kindly correct me if I missed something.

Please find the screenshot : There is no space in the label.

enter image description here

Upvotes: 2

Views: 1278

Answers (3)

Johnny Rockex
Johnny Rockex

Reputation: 4196

Here is code for adding a background to a UILabel, but it detects newlines by looking at what point the width of a text exceeds that of the label.

-(CAShapeLayer *)backgroundLayerForLabel:(UILabel *)label padding:(float)padding{

    NSString * text = label.text;
    NSArray * words = [text componentsSeparatedByString:@" "];

    UILabel * sizeLabel = [UILabel new];
    sizeLabel.frame = CGRectMake(0, 0, label.frame.size.width, 0);
    sizeLabel.font = label.font;
    [sizeLabel setText:@" "]; //just to get row height

    float rowHeight = sizeLabel.intrinsicContentSize.height;
    float runWidth = 0.0f;
    float maxWidth = ceilf(label.frame.size.width);

    float startingX = label.frame.origin.x - padding;
    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(startingX, label.frame.origin.y)];

    for (NSString * word in words){

        [sizeLabel setText:[NSString stringWithFormat:@"%@", word]];
        float sizeWidth = sizeLabel.intrinsicContentSize.width;

        [sizeLabel setText:[NSString stringWithFormat:@"%@ ", word]];
        float sizeSpaceWidth = sizeLabel.intrinsicContentSize.width;

        float nextWidth = floorf(sizeWidth + runWidth);
        if (nextWidth > maxWidth){ //spans next line

            [path addLineToPoint:CGPointMake(startingX + runWidth + padding, path.currentPoint.y)];
            [path addLineToPoint:CGPointMake(path.currentPoint.x, path.currentPoint.y + rowHeight)];

            runWidth = sizeSpaceWidth;

        } else {

            runWidth += sizeSpaceWidth;
        }

    }
    [path addLineToPoint:CGPointMake(startingX + runWidth + padding, path.currentPoint.y)];
    [path addLineToPoint:CGPointMake(path.currentPoint.x, path.currentPoint.y + rowHeight)];
    [path addLineToPoint:CGPointMake(startingX, path.currentPoint.y)];
    [path closePath];

    CAShapeLayer * layer = [CAShapeLayer layer];
    layer.path = path.CGPath;
    layer.fillColor = [UIColor whiteColor].CGColor;
    return layer;

}

Where nextWidth > maxWidth, that's where your newline kicks in, with the runWidth variable being the length of the sentence. Use like this:

    UILabel * label = [UILabel new];
    label.frame = CGRectMake(20, localY, labelWidth, 0);
    label.font = [UIFont systemFontOfSize:24.0f weight:UIFontWeightSemibold];
    label.textColor = [UIColor blackColor];
    label.text = @"This is a really long quotes. I know. a. b. c. defghi. jklmn. op. q. r. stuv. ";
    label.numberOfLines = 4;
    label.lineBreakMode = NSLineBreakByWordWrapping;
    [label sizeToFit];

    CAShapeLayer * layer = [self backgroundLayerForLabel:label padding:5.0f];
    layer.shadowColor = [UIColor blackColor].CGColor;
    layer.shadowOffset = CGSizeMake(0, 1);
    layer.shadowRadius = 2.0f;
    layer.shadowOpacity = 0.8f;
    [cell.layer addSublayer:layer];
    [cell addSubview:label];

Giving you something like this:

enter image description here

Upvotes: 0

Ankit Kumar Gupta
Ankit Kumar Gupta

Reputation: 4042

I solved this issue using this method :

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

    let text:NSString = label.text! as NSString
    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: CGFloat(0), y: CGFloat(0), width: CGFloat(rect.size.width), height: CGFloat(100000)), transform: .identity)

    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
}

This method returns an array of string that are on different lines. Now you can use this array as per your need to create a new string with \n join.

My usage :

            var stringArray = [String]()

            for newItem in self.getLinesArrayOfStringInLabel(label: item.labelToAdd) {
                stringArray.append(newItem.replacingOccurrences(of: "\n", with: ""))
            }
            let stringToSend = stringArray.joined(separator: "\n")
            print(stringToSend)

Feel free to ask any detail. Thanks

Upvotes: 1

gEeKyMiNd
gEeKyMiNd

Reputation: 245

Here is the code work around for detecting line breaks inside 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"];
}

How to use :

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);

You just need to set parameters required by getLinebreaksWithString and you will get a string with each "\n" break included. Use your label width and starting point you designed in storyboard.

Reference : https://stackoverflow.com/a/15243686/6904341

Upvotes: 0

Related Questions