I have a UILabel which usually has to display one or two words.
Many times one of the words doesn't fit into one line, so I would like to reduce font size in order to fit each word at least in one line (not breaking by character).
Using the technique described in http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/
self.numberOfLines = 2;
self.lineBreakMode = NSLineBreakByTruncatingTail;
self.adjustsFontSizeToFitWidth = YES;
self.minimumScaleFactor = 0.65;
I've found that it plays well when the second word doesn't fit in just one line.
But it doesn't when there is just one word, or the first word is the one
that doesn't fit.
I managed to solve the case of just one word doing this:
-(void)setText:(NSString *)text
{
self.numberOfLines = [text componentsSeparatedByString:#" "].count > 1 ? 2 : 1;
[super setText:text];
}
But how could I solve those cases where the first word doesn't fit?? Any ideas?
How about this ?
self.numberOfLines = [text componentsSeparatedByString:#" "].count;
[self setAdjustsFontSizeToFitWidth:YES];
But, this will rule out the case where your label text consists of two very small words, eg."how are". In such cases, the entire string will be visible in the first line itself. If it is your requirement to display each word in a separate line then i would recommend you adding a '\n' after every word. This means that you will have to edit the string before assigning it to the label. Thus, a universal solution could be like :
NSString *string = #"how are"; //Let this be the string
NSString *modifiedString = [string stringByReplacingOccurrencesOfString:#" " withString:#"\n"];
[self setText:modifiedString];
[self setTextAlignment:NSTextAlignmentCenter];
[self setAdjustsFontSizeToFitWidth:YES];
[self setNumberOfLines:0];
self.lineBreakMode = NSLineBreakByWordWrapping;
Related
In my app, I'm trying to have the UILable time text aligned in a way like WhatsApp where if the last sentence of a UITextView's message text is too long the time would be pushed to the next line. So I'm actually trying to align the UILabel's time according to the UITextView's message box.
message box time layout image
This is a text message. 7:16PM
This is a longer
text message. 7:15PM
This is an even longer
text message till end.
7.15PM
One way I could think of was to use UITextView's class and grab the length of the last line and calculate the text width as compared with the UITextView's width to know if it exceeds but no luck. Is there any way?
This is the code that I wrote which solved my issue. Thanks to Greg for pointing me in the right direction.
+ (BOOL)didTextMoveToNewline:(NSString *)text previousSize:(CGSize)previousSize {
UIFont *messageBubbleFont = [UIFont systemFontOfSize:14.0f];
float maximumTextWidth = 188;
NSString *finalText = [[NSString alloc] init];
finalText = [NSString stringWithFormat:#"%# 10:00PM ", text];
CGRect stringRect = [finalText boundingRectWithSize:CGSizeMake(maximumTextWidth, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:#{ NSFontAttributeName : messageBubbleFont }
context:nil];
CGSize stringSize = CGRectIntegral(stringRect).size;
if (stringSize.height > previousSize.height)
return YES;
else
return NO;
}
The hack would be to automatically add a blank space the length of the time to the end of every message.
let userMessageLabel.text = userMessage + " "
Then let the time always overlap the last line. If the amount of spaces is correct, the text and time will never touch.
I think it's a common issue when you have a set of words where you don't want a break line.
Sometimes the character between those words is a space or a hyphen, etc. In my case it's a point :)
This is my text 50.0/80.0
At the end I did it using the size label and measuring how much space I need for that string in particular:
UIFont *fontAwardNumber = [UIFont fontWithName:#"DIN-Bold" size:20];
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize labelSize = (CGSize){customCell.awardValueLabel.bounds.size.width, FLT_MAX};
CGRect rectNeededForAwardNumber = [awardNumber boundingRectWithSize:labelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: fontAwardNumber} context:context];
if (rectNeededForAwardNumber.size.height > customCell.awardValueLabel.bounds.size.height) {
//We need to add a breakline
NSRange range = [awardNumber rangeOfString:#"/"];
if (range.location != NSNotFound) {
awardNumber = [awardNumber stringByReplacingCharactersInRange:range withString:#"/\n"];
}
}
I found other solutions like replacing your space or hyphen for unbreakable characters:
Preventing line breaks in part of an NSAttributedString
But my question is more general, does NSAttributedString provide something to define a set of words as non breakable? Or is there any easier way to do it for a general set of words?
No, NSAttributedString doesn't have any per-character attributes that preventing line breaking within a range. You can set the NSLineBreakMode to ByClipping or another non-wrapping mode in the NSParagraphStyle, but that applies to all the text in the paragraph. (Paragraphs are separated by newlines.)
To prevent line breaking in a smaller range than a whole paragraph, you need to insert a U+2060 WORD JOINER between any two characters where an unwanted break might occur. In your example, that means on each side of the slash character.
I'm still sort of new to Xcode, so please be patient with me. Anyway, I'm having a bit of trouble trying to display the whole contents of an array in a UILabel. I'm able to display it by simply using the code
wordList.text = [NSString stringWithFormat:#"List of Words:\n %#", listA];
However upon running, the label ends up displaying a parenthesis and the words on their own lines, as well as quotation marks around the words, and the ending quotation mark and a comma in the line between each word. Example:
List of Words:
(
"apple
",
"banana
",
"etc.
While I do want the words to be displayed in their own lines, I do not want the parenthesis and the closing quotation mark and comma being displayed in a separate line. I would also prefer removing the parenthesis, quotation marks, and commas all together, but I wouldn't mind too much if I'm unable to.
Could anyone please explain why its being displayed as such, and to help me correctly display each word of an array in its own line in a UILabel?
Use this:
NSArray *listOfWords = #[#"One", #"Two", #"Three"];
NSString * stringToDisplay = [listOfWords componentsJoinedByString:#"\n"];
wordList.text = stringToDisplay;
Will Display:
One
Two
Three
The parentheses, quotation marks, and commas are being added because providing an array as an argument to the format specifier %# causes the -(NSString *)description method to be sent to the array. NSArray overrides NSObject's implementation of description and returns a string that represents the contents of the array, formatted as a property list. (As opposed to just returning a string with the array's memory address.) Hence, the extra characters.
You Can use this Code
NSArray *listOfWords = [NSArray arrayWithObjects:
#"one.",
#"two.",
nil];
for (NSString *stringToDisplay in matters)
{
//frame, setting
labelFrame.origin.x = 20.0f;
UILabel *stringToDisplayLabel = [[UILabel alloc] initWithFrame:labelFrame];
stringToDisplayLabel.backgroundColor = [UIColor clearColor];
stringToDisplayLabel.font = [UIFont boldSystemFontOfSize:12.0f];
stringToDisplayLabel.lineBreakMode = NSLineBreakByWordWrapping;
stringToDisplayLabel.numberOfLines = 0;
stringToDisplayLabel.textColor = [UIColor whiteColor];
stringToDisplayLabel.textAlignment = NSTextAlignmentLeft;
//set up text
stringToDisplayLabel.text = stringToDisplay;
//edit frame
[stringToDisplayLabel sizeToFit];
labelFrame.origin.y += stringToDisplayLabel.frame.size.height + 10.0f;
[self.view addSubview:stringToDisplayLabel];
[matterLabel release];
}
In my application i need to align all the paragraph differently.
like, first paragraph's headIndent is 0.0f then second's 10.0f and third's 3.0f.
i am giving all the paragraph style to textview.attributedText. and it took only one style.
Here whole text will come dynamically by Typing. means when User will type in text view at that time. so, there are no static string to do this.
I am placing all the characters in UITextView by this...
UIFont *fontBold = [UIFont fontWithName:#"Helvetica-Bold" size:15];
attributesHelveticaBold = #{NSFontAttributeName :fontBold};
UIFont *fontNormal = [UIFont fontWithName:#"HelveticaNeue-Light" size:15];
attributesNormal = #{NSFontAttributeName :fontNormal};
if (varBold== 1) {
[textView setTypingAttributes:attributesHelveticaBold];
}
else {
[textView setTypingAttributes:attributesNormal];
}
And i want to get this kind of result in text view
When i am typing the typing become slow too.
but i think i'll come over that issue but for now i stuck on this alignment problem.
how to do it when bullet point come and when different text come.
any kind of link, code, tutorial will be great help...
---------- Edit : ----------
Please have a look in Evernote's application.
I need to do the exactly same thing in my app. for alignment of second,third,etc line when bullet come.
-------- Edit after searching :-------
I searched too much for this but ain't find anything by googling.
So, now i am asking if anyone now about How to give any paragraph style or any attribute style to a paragraph and just leave it as it is on text view and then perform other paragraph style on second paragraph. at this time the first paragraph will not pass throw "shouldChangeTextInRange" method.
yes, it's quite confusing whatever i am saying.
so i explaining it in general...
if user set the text view's first paragraph's headIndent=7.0f then when user will type next paragraph and set the headIndent = 13.0f then first paragraph will stay as it is in textview and just running paragraph will come in a chapter (means in a method).
right now i am doing these thing in shouldChangeTextInRange method to do style for each paragraph.
varStaringPointOfString = 0;
varEndingPointOfString = 0;
NSArray *sampleArrToGetattrStr = [txtViewOfNotes.text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (int i=0; i<[sampleArrToGetattrStr count]; i++)
{
NSString *strtostoreLength = [NSString stringWithFormat:#"%#",[sampleArrToGetattrStr objectAtIndex:i]];
varStaringPointOfString = (int)strtostoreLength.length + varEndingPointOfString;
if ([strtostoreLength hasPrefix:#"\t•\t"])
{
[[textView textStorage] addAttribute:NSParagraphStyleAttributeName value:paragraphStyleForBullet range:NSMakeRange(varEndingPointOfString, strtostoreLength.length)];
}
else
{
[[textView textStorage] addAttribute:NSParagraphStyleAttributeName value:paragraphStyleNormal range:NSMakeRange(varEndingPointOfString, strtostoreLength.length)];
}
varEndingPointOfString = varStaringPointOfString;
strtostoreLength =#"";
}
but from this the speed of typing is become very slow.
Try this:
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init];
[paragraphStyle setFirstHeadLineHeadIndent:firstLineIndend]; //Only the first line
[paragraphStyle setHeadIndent:headIndent]; //The rest of the lines, except the first one
[yourAttributedString addAttribute:NSParagraphStyleAttributeName
value:paragraphStyle
range:paragraphRange];
For the bullet point, that's something different. You need to find where are the bullet point, and set another indent accordingly.
I am creating a Point-of-Sale (POS) receipt and need to have item names with a certain width and need to have a price on the right (like float-right in CSS).
I am using https://github.com/FuerteInternational/FTCoreText to create the content. Any ideas how to achieve this without having to create two views and place them on top of each other?
Just to clarify, I need to have this:
Coffee £1.45
milk, skinny
Danish Pastry £1.75
Coca Cola £1.10
Full English Bfst £12.50
So pretty much I need a string to be floating on the left and other string floating on the right ...
The only thing I can think of is inserting in each string f.e. 30 spaces after the first string and assigning wordWrap = truncate middle. Not so sure that this is exactly what you're looking for.
I agree with Brian Shamblen it'l be pretty easy to do with 2 labels.
It looks like what you want isn't really a second column, which Core Text cannot do out of the box, but rather a tabular layout (like good ol' <table>). Fortunately tabular layout can be acheived in Core Text by using tab stops. The only 2 things you need are:
the CHARACTER TABULATION (U+0009) character,
and a paragraph style with the correct tab stops setting.
That can be done this way:
NSString *string = #"Coffee\t£1.45\n milk, skinny\nDanish Pastry\t£1.75\nCoca Cola\t£1.10\nFull English Bfst\t£12.50";
CGFloat width = 200;
NSArray *tabs = #[CFBridgingRelease(CTTextTabCreate(kCTTextAlignmentRight, width, NULL))];
CTParagraphStyleSetting setting = {kCTParagraphStyleSpecifierTabStops, sizeof(tabs), &tabs};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(&setting, 1);
NSDictionary *attributes = #{CFBridgingRelease(kCTParagraphStyleAttributeName): CFBridgingRelease(paragraphStyle)};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
There are two caveats:
You need to know the width of the view when creating the paragraph style.
Core Text won't wrap lines on tab stops which means you have to make sure your text is short enough to fit.
Perhaps you could do the following with just one UILabel per line:
Define a left word like "Coffee" and a right one like "£1.45".
Make a string by concatenating the left word with a space and the
right one.
Define the width and height of the frame that you want the label with that
string to be contained in, with CGSizeMake(width, height).
Estimate the size of that label using sizeThatFits providing the
CGSize constraints that you want as an argument.
If the estimated width of the label is smaller than the required
width, increase the size of your label, by adding a space to your
string and repeat.
In code:
NSArray *leftWords = #[#"Coffee", #" milk, skinny", #"Danish Pastry", #"Coca Cola", #"Full English Bfst"];
NSArray *rightWords = #[#"£1.45", #"", #"£1.75", #"£1.10", #"£12.50"];
CGFloat xOffset = 5;
CGFloat yOffset = 60;
CGFloat labelHeight = 44;
CGFloat labelWidth = 300;
for (int i = 0; i < leftWords.count; i ++) {
UILabel *aLabel = [[UILabel alloc] initWithFrame:CGRectMake(xOffset, yOffset + i * labelHeight, labelWidth, labelHeight)];
aLabel.numberOfLines = 1;
aLabel.textAlignment = NSTextAlignmentLeft;
aLabel.layer.borderColor = [UIColor greenColor].CGColor;
aLabel.layer.borderWidth = 1.0f;
NSString *leftWord = [leftWords objectAtIndex:i];
NSString *rightWord = [rightWords objectAtIndex:i];
NSMutableString *spaces = [[NSMutableString alloc] initWithString:#" "];
const CGFloat MAX_WIDTH = 310;
CGSize maxLabelSize = CGSizeMake(MAX_WIDTH, CGFLOAT_MAX);
CGSize expectedSize = CGSizeMake(0, 0);
CGFloat expectedWidth = 0.0;
NSString *phrase = #"";
do {
phrase = [NSString stringWithFormat:#"%#%#%#", leftWord, spaces, rightWord];
aLabel.text = phrase;
expectedSize = [aLabel sizeThatFits:maxLabelSize];
expectedWidth = expectedSize.width;
// Increase spaces
[spaces appendString:#" "];
} while (expectedWidth < MAX_WIDTH);
CGRect frame = aLabel.frame;
frame.size = expectedSize;
aLabel.frame = frame;
[self.view addSubview:aLabel];
}
I have placed the above within my viewDidLoad and did #import <QuartzCore/QuartzCore.h> to allow marking of the border of the UILabel.
Here is a screenshot of my working code:
It works for the "milk, skinny" line too as the text alignment of the label is set to NSTextAlignmentLeft.
I recently came across this problem and found no answers.. so I created a class for anyone that needs it! Let's say you want to create a list of strings like the example in the question:
- (void)createReceipt {
NSString *coffee = [LRString LRStringWithLeft:#"Coffee" right:#"£1.45" maxlength:29 maxleft:16 maxright:11];
}
This "coffee" string would look like this:
Coffee £1.45
"maxlength" is the maximum length of your string
"maxleft" is the maximum length of your left string (coffee)
"maxright" is the maximum length of your right string (price)
If the name exceeds the "maxleft" length, then the last 3 letters of the name (coffee) will be replaced with "..."
Check it out on GitHub here: https://github.com/iosdec/LRString