Calculate size of NSAttributedString from HTML - objective-c

I am using a UITextView to display an NSAttributedString from some given HTML, which can includes elements such as bold, italicized, lists, marked, super & subscript, etc.
Currently the code below works pretty well for just paragraphs of text, but once I start adding more complicated elements such as lists and line breaks, the sizing is completely off.
// Create the NSMutableAttributedString from given HTML
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *options = #{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)};
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithData:data options:options
documentAttributes:nil error:nil];
// Max size for the text container (no limit on height)
CGSize bounds = CGSizeMake(320.0, CGFLOAT_MAX);
// Set the font and size for better rendering results
UIFont *font = [UIFont fontWithName:#"Roboto" size:14.0];
NSDictionary *attrFont = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
NSRange range = NSMakeRange(0, str.length);
[str addAttributes:attrFont range:range];
// Calcualte the size of the UITextView based on the above parameters
CGRect rect = [str boundingRectWithSize:bounds options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading| NSStringDrawingUsesDeviceMetrics context:nil];
I've done some searching and found this thread, but after trying what is suggested over there it still doesn't appear to be working, wondering if anyone knows of a better way to do this?
Calculate Height Of NSAttributedString Containing HTML

Ok after much fiddling around I found that the sizes are actually correct, but the UITextView has some padding / insets that cause the overflow. Setting the following on the textView fixed the problem
[self.textView setTextContainerInset:UIEdgeInsetsZero];
self.textView.textContainer.lineFragmentPadding = 0;

Related

Justify text for UILabel in iOS 9

I have found solution to justify text in UILabel for versions up to iOS 8.4 through attributed strings: set label string as attributed and modify hyphenation value as presented below.
This solution stopped working on iOS 9 (text shows left aligned). I need other working solution which supports from iOS 7 or at least works at iOS 9 (would add 'if' somewhere)
Update:
It seems that adding a #"locale" key to attributes of NSAttributedString may help. See here.
Original answer:
You can always use NSAttributedString here:
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.hyphenationFactor = 1;
paragraphStyle.alignment = NSTextAlignmentJustified;
NSDictionary *attributes = #{
NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleBody],
NSParagraphStyleAttributeName: paragraphStyle
};
NSString *string = #"your text here";
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
self.label.attributedText = attributedString;
self.label.numberOfLines = 0;

Convert NSString to HTML and set HTML text into UILabel [duplicate]

This question already has answers here:
How to create a UILabel or UITextView with bold and normal text in it?
(5 answers)
Closed 9 years ago.
Is it possible to convert a NSString to html and set as a label?
The code below shows the NSString I want to set finalPrice as bold text and finalStr&shipping string as normal text
NSString *myText = [NSString
stringWithFormat:
#"%#\nFinal price including $%.2f Shipping and all discount: <b>$%.2f</b>",
finalStr,shipping,finalPrice];
lbl.text = myText;
I want to set multiple color and multiple text type into same dyanamic label.
use following label for bold effects. Or you can get code from that class.
DAAttributedStringUtils
and also see this
Different Label
Edit
NSString *myText = [NSString stringWithFormat:#"%#\nFinal price including $%.2f Shipping and all discount: %%B$%.2f%%b",finalStr,shipping,finalPrice];
DAAttributedLabel* lbl = [[DAAttributedLabel alloc] initWithFrame:CGRectMake(30.0f, 30.0f, 260.0f, 24.0f)];
lbl.backgroundColor = [UIColor colorWithRed:0.9f green:0.9f blue:1.0f alpha:1.0f];
lbl.text = (id)[formatter formatString:myText];
[self.view addSubview:lbl];
Try using NSAttributedString
There are already several questions around this here like
How do you use NSAttributedString?
NSString * textString = #"Hello Bold";
NSInteger _stringLength = [textString length];
NSMutableAttributedString * attString = [[NSMutableAttributedString alloc] initWithString:textString];
[attString addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"Helvetica" size:14.0f]; range:NSMakeRange(0, _stringLength)];
[attString addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"Helvetica-Bold" size:14.0f]; range:NSMakeRange(6, 4)];
myLabel.attributedText = attString;
(code not tested)
Edit:
label.attributedText is only available for iOS 6.0+
FYI, the answer above suggesting the use of DAAttributedStringUtils and DAAttributedLabel didn't mention that these are convenience classes for the use of NSAttributedString. They make formatting NSAttributedString instances a little easier. As an example, here's how to do the same formatting described about by HAS using DAAttributedStringUtils:
float finalPrice = 34.99, shipping = 4.99;
// Setup the formatter
DAAttributedStringFormatter* formatter = [[DAAttributedStringFormatter alloc] init];
formatter.defaultFontFamily = #"Georgia";
formatter.defaultFontSize = 12.0f;
formatter.colors = #[ [UIColor blackColor], [UIColor redColor] ];
NSAttributedString* attrStr = [formatter formatString:#"%0C%0FRed Courier Text %1C%1FBlue Arial Text %0CRed Arial Text"];
// setup base strings
NSString *finalStr = #"Some Text. ";
NSString *shippingAttributed = [NSString stringWithFormat:#"%%B%%1C$%.2f%%b%%c", shipping];
NSString *middleText0 = #"Final price including ";
NSString *middleText1 = #" Shipping and all discount: ";
NSString *finalPriceAttributed = [NSString stringWithFormat:#"%%B%%1C$%.2f%%b%%c", finalPrice];
// Format the strings
self.label.attributedText = [formatter formatString:[NSString stringWithFormat:#"%#%#%%B%%1C%#%%b%%c%#%%B%%1C%#", finalStr, shippingAttributed, middleText0, middleText1, finalPriceAttributed];
Somewhat less code, and I think easier to understand. FYI, the formatter string in the last line contains codes that are used to modify the format of portions of the string. Those codes use double percents (

Is it possible to apply NSAttributedString to CPTTextLayer's Text

I want to display a "custom label" for each index of CPTScatterPlot. CPTTextLayer takes the textcolor, font size, etc from textStyle property. I want to display each character of the string which is displayed in the CPTTextLayer with a different color. I know it is possible using NSAttributedString but when I passed this argument to [[CPTTextLayer alloc] initWithText:attributedStr], my application crashed. Is it possible to apply a NSAttributedString to CPTTextLayer's text?
As of version 1.3 CorePlot does allow creating CPTTextLayer based upon NSAttributedString. Here's example:
NSString *smallTextPart = #"Small part ";
NSString *bigTextPart = #"and the bigger one";
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"%#%#", smallTextPart, bigTextPart]];
[str addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"HelveticaNeue" size:12.0] range:NSMakeRange(0, smallTextPart.length)];
[str addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"HelveticaNeue-Bold" size:18.0] range:NSMakeRange(smallTextPart.length, bigTextPart.length)];
CPTTextLayer *txtLayer = [[CPTTextLayer alloc] initWithAttributedText:str];
It also works properly with multi line CPTTextLayer labels (you can split CPTTextLayer into separate lines using '\n' character).
Cheers.
Core Plot currently does not support attributed text. You can add an enhancement request to the Core Plot issue tracker so that this feature is considered for inclusion in a future version.

How to make subscripts and superscripts using NSAttributedString?

I need to make subscripts for chemistry formulas (H2O, Na^2+, etc)?
Is this possible to do with NSAttributedString, or is there an alternative/easier way to make subscripts?
Here's what I did in iOS 6. First add the CoreText, and QuartzCore frameworks. Then import:
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CTStringAttributes.h>
#import <CoreText/CoreText.h>
I made a small function that inputs a plain NSString and exports a NSMutableAttributedString with the last character in superscript. This can be modified to allow setting superscript or subscript, change kCTSuperscriptAttributeName value to -1. Also you could add a variable to specify where to put the superscript in the string. Right now it just assumes the end of the string.
- (NSMutableAttributedString *)plainStringToAttributedUnits:(NSString *)string;
{
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:string];
UIFont *font = [UIFont systemFontOfSize:10.0f];
UIFont *smallFont = [UIFont systemFontOfSize:9.0f];
[attString beginEditing];
[attString addAttribute:NSFontAttributeName value:(font) range:NSMakeRange(0, string.length - 2)];
[attString addAttribute:NSFontAttributeName value:(smallFont) range:NSMakeRange(string.length - 1, 1)];
[attString addAttribute:(NSString*)kCTSuperscriptAttributeName value:#"1" range:NSMakeRange(string.length - 1, 1)];
[attString addAttribute:(NSString*)kCTForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, string.length - 1)];
[attString endEditing];
return attString;
}
Now when I want to use it I can do the following to put it in a UITextField:
NSString *qlwUnitsPlainText = #"m3";
self.quantityLoadWeightUnits_textField.attributedText = [self plainStringToAttributedUnits:qlwUnitsPlainText];
I hope this helps somebody else, there's not many examples out there!
This is possible to do with NSAttributedString. The attribute constant you're looking for depends on your platform. For Mac OS X it is NSSuperscriptAttributeName and on iOS it is kCTSuperscriptAttributeName. Pass in a negative value for subscript.
The only caveat is that UILabel on iOS can't draw NSAttributedStrings (yet, fingers crossed for iOS 6). You would need to draw the text using Core Text or find some third party replacement for UILabel that can draw an NSAttributedString.
On iOS, I had missed the kCTSuperscriptAttributeName constant but had good results with font size and "baseline". It gives you a little more control too for less obedient fonts:
+ (NSAttributedString *)attributedStringForText:(NSString *)normalText andSuperscript:(NSString *)superscriptText textSize:(CGFloat)textSize
{
UIFont *normalFont = [Styles mainFontWithSize:textSize];
UIFont *superFont = [Styles mainFontWithSize:textSize / 2];
NSMutableAttributedString *finalStr = [[NSMutableAttributedString alloc] initWithString:normalText attributes:#{NSFontAttributeName: normalFont}];
NSAttributedString *superStr = [[NSAttributedString alloc] initWithString:superscriptText attributes:#{NSFontAttributeName: superFont, NSBaselineOffsetAttributeName:#(textSize/2)}];
[finalStr appendAttributedString:superStr];
return finalStr;
}
For SubScript use value for kCTSuperscriptAttributeName as #-1.
As per the document
#discussion Value must be a CFNumberRef. Default is int value 0. If
supported
by the specified font, a value of 1 enables superscripting and a
value of -1 enables subscripting.
extern const CFStringRef kCTSuperscriptAttributeName
CT_AVAILABLE(10_5, 3_2);
Example- [lblHeader setText:#“Headers [Alpha1 – text”];
NSMutableAttributedString *headerSubscript = [[NSMutableAttributedString alloc]initWithAttributedString: lblHeader.attributedText];
[headerSubscript addAttribute:(NSString *)kCTSuperscriptAttributeName value:#-1 range:NSMakeRange(14,1)];
[lblHeader setAttributedText:headerSubscript];
you can also do the following if you want to make it a litle cleaner
NSDictionary *attr = #{ NSFontAttributeName: smallfont,
(NSString*)kCTSuperscriptAttributeName: #1 }
NSRange fabricWeightRange = NSMakeRange(fabricWeight.location + 2, 1);
[subKeyString setAttributes:attr range:fabricWeightRange];

Cocoa get string width in pixels from font

I am trying to find the width in pixels of a string from the font and font size. I am currently using this code, but it is not working 100% of the time. Is there another way to do it?
NSSize textSize = [aTextLayer.string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:#"Bank Gothic Medium", NSFontNameAttribute, [NSNumber numberWithFloat:aTextLayer.fontSize], NSFontSizeAttribute, nil]];
NSAttributedString is granted a -size method by the Application Kit Additions.
NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
#"Bank Gothic Medium", NSFontNameAttribute,
[NSNumber numberWithFloat:aTextLayer.fontSize], NSFontSizeAttribute,
nil];
NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:aTextLayer.string attributes:attributes];
NSSize size = attributedString.size;
Here is what i use to get the size of a string...
NSSize size = [#"Some text" sizeWithAttributes:[NSDictionary dictionaryWithObject:[NSFont fontWithName:#"Helvetica Neue Bold" size:24.0f] forKey:NSFontAttributeName]];
NOTE: If you are adding the string to a textfield, i have found that you need to add about 10 to size.width for it to fit.
Try using the actual NSFont (or UIFont) object instead of just the name of the font.
Here is yet another example:
NSString *test = [[NSString alloc] initWithFormat:#"%u:%u:%u.000", hours, minutes, seconds];
NSSize boundingSize = {100,300}; //I suppose this is the constraints?
NSRect boundingRect = [test boundingRectWithSize:boundingSize options:NULL attributes:stringAttributes];
point.x -= boundingRect.size.width; //This point points at the end of screen
[test drawAtPoint:point withAttributes:stringAttributes];
Here is for the stringAttributes, that may help noobs like me:
NSMutableDictionary *stringAttributes;
stringAttributes = [NSMutableDictionary dictionary];
[stringAttributes setObject:[NSFont fontWithName:#"Monaco" size:16] forKey:NSFontAttributeName];
[stringAttributes setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
[stringAttributes setObject:[NSColor blackColor] forKey:NSBackgroundColorAttributeName];