Is it possible to to have more than one color in the same UILabel - objective-c

I want more than one font color in the same UILabel. I dont know if it is possible. I dont really think so but maybe some of you out there have a smart solution for it? Maybe something like stringWithFormat. [NSString stringWithFormatAndColor: #"Text with color: %# %#", text, color, text, color]
This image illustrate what I'm trying to accomplish:

You can achieve this with NSAttributedSting. An easy to use drop-in replacement for UILabels with support for attributed strings is TTTAtributedLabel or OHAttributedLabel
In my experience it is easier to work with NSMutableAttributedStrings and build it up step by step.
NSMutableAttributedString *attrStr = [NSMutableAttributedString attributedStringWithString:#""];
NSMutableAttributedString *a = [NSMutableAttributedString attributedStringWithString:#"This is "];
[a setTextColor:aColorObj];
NSMutableAttributedString *b = [NSMutableAttributedString attributedStringWithString:#"only one "];
[b setTextColor:bColorObj];
NSMutableAttributedString *c = [NSMutableAttributedString attributedStringWithString:#"Label"];
[c setTextColor:cColorObj];
[attrStr appendAttributedString:a];
[attrStr appendAttributedString:b];
[attrStr appendAttributedString:c];
OHAttributedLabel *attributedTextLabel = [[OHAttributedLabel] initWithFrame:frame]
[attributedTextLabel setAttributedText:attrStr];

Related

Calculate size of NSAttributedString from HTML

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;

Getting the attribute dictionaries from a NSAttributedString

How can I obtain the attributes of an NSAttributedString?
I thought using enumerateAttributesInRange:NSMakeRange(0, str.length) and then saving every attributes in a NSMutableDictionaries but I'd like to know if exists a better way.
EDIT------
My problem is that I have a NSAttributedString and i want to use this function on its attributes...
CTFramesetterSuggestFrameSizeWithConstraints(framesetter,range, __ATTRIBUTES__, size , NULL);
NSDictionary *attributesFromString = [string attributesAtIndex:0 longestEffectiveRange:nil inRange:NSMakeRange(0, string.length)];
UITextView *t = [[UITextView alloc] init];
[t setAttributedText:a];
t.typingAttributtes

Custom NSFormatter in Objective C

I am trying to write my own custom formatter in Objective C by subclassing NSNumberFormatter. Specifically what I'd like to do is make a number turn red if it is above or below certain values. The apple documentation says
For example, if you want negative financial amounts to appear in red, you have this method return a string with an attribute of red text. In attributedStringForObjectValue:withDefaultAttributes: get the non-attributed string by invoking stringForObjectValue: and then apply the proper attributes to that string.
Based on this advice I implemented the following code
- (NSAttributedString*) attributedStringForObjectValue: (id)anObject withDefaultAttributes: (NSDictionary*)attr;
{
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:[self stringForObjectValue:anObject]];
if ([[attrString string] floatValue] < -20.0f) {
[attrString addAttribute:#"NSForegroundColorAttributeName" value:[NSColor redColor] range:NSMakeRange(0, 10)];
return attrString;
} else return attrString;
}
But when I test this all it does is freeze my application. Any advice would be appreciated. Thanks.
I believe this has something to do with your NSRange that you create. I believe your length (10 in your example) is out of bounds. Try getting the length of the string that you use to initialize your NSMutableAttributedString.
For example:
- (NSAttributedString*) attributedStringForObjectValue: (id)anObject withDefaultAttributes: (NSDictionary*)attr;
{
NSString *string = [self stringForObjectValue:anObject];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:string];
NSInteger stringLength = [string length];
if ([[attrString string] floatValue] < -20.0f)
{
[attrString addAttribute:#"NSForegroundColorAttributeName" value:[NSColor redColor] range:NSMakeRange(0, stringLength)];
}
return attrString;
}
Here is how I was finally able to implement this. To make it more visible when a number is negative, I decided to make the background of the text red with white text. The following code does work in a NSTextField cell. I'm not sure why the code in my question (and the answer) does not work, addAttribute should work.
- (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes: (NSDictionary *)attributes{
NSString *string = [self stringForObjectValue:anObject];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:string];
NSInteger stringLength = [string length];
if ([[attrString string] floatValue] < 0)
{
NSDictionary *firstAttributes = #{NSForegroundColorAttributeName: [NSColor whiteColor],
NSBackgroundColorAttributeName: [NSColor blueColor]};
[attrString setAttributes:firstAttributes range:NSMakeRange(0, stringLength)];
}
return attrString;
}

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