Reputation: 4042
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.
Upvotes: 2
Views: 1278
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:
Upvotes: 0
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
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