NSAttributedString with link - objective-c

I have string with 2 words. I want to color 2 words with different colors say red and green.
Also it the whole string should have clickable link. I tried with following code, the link attribute overrides the previous colors.
NSString *str = #"xxxx yyyyyy";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:str];
[attStr addAttribute:NSForegroundColorAttributeName
value:[UIColor redColor] range:NSMakeRange(0, 4)];
[attStr addAttribute:NSForegroundColorAttributeName
value:[UIColor blueColor] range:NSMakeRange(5, str.length-5)];
NSMutableAttributedString *clickStr = [[NSMutableAttributedString alloc] initWithAttributedString:attStr];
[clickStr addAttribute:NSLinkAttributeName value:#"textLink:link" range:NSMakeRange(0, str.length)];

As far as I know, you can't override the link style in a default attributed string. You'll have to use something like TTTAttributedLabel though it does have some bugs on iOS 8 still.

Related

Objective C label line spacing?

Is there a way to set the distance of two lines within a UILabel?
I tried to do it within Interface Builder but without success.
The code you want will be something like this:
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:#"Sample text"];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setLineSpacing:24];
[attrString addAttribute:NSParagraphStyleAttributeName
value:style
range:NSMakeRange(0, strLength)];
uiLabel.attributedText = attrString;
You can use NSAttributedString to add spacing between two lines within a UILabel:
NSString *labelText = #"My String";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:labelText];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:20];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [labelText length])];
cell.label.attributedText = attributedString ;
OR
If you are using storyboard then you can control line spacing in the storyboard by selecting text type is attributed and add spacing value:
Since iOS 6, Apple added NSAttributedString to UIKit, making it possible to use NSParagraphStyle to change the line spacing.
Alternatively, you can do this via Storyboards using Attributed Text and then clicking the ... symbol. See link below for screenshot.
https://i.stack.imgur.com/aiNfR.png

NSTextView: format text programmatically

How can I format parts of a text in a NSTextView programmatically? For example, make all occurrences of the word "foo" blue or make all the text grey but the current paragraph (where the cursor is) solid black?
Thanks
You can use NSAttributedString or NSMutableAttributedString just specify text format (color, text size, etc) and pass it to your NSTextView, for example:
NSString *str = #"test string";
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString: str];
NSDictionary *attributes = #{
NSForegroundColorAttributeName : [NSColor redColor],
NSFontAttributeName : [NSFont fontWithName:#"HelveticaNeue-Bold" size:12.0]
};
[attrString setAttributes:attributes range:NSMakeRange(0, str.length)];
[[self.myNSTextView textStorage] appendAttributedString: attrString];
That will set up all your text to be the same but just replace range attribute with the one you want to change only this part of the text:
NSMakeRange(0, 2) this will add just the text attributes to first two lerrers.

Applying different colors to certain parts of SKLabelNode's text

I am pretty much sure that for this can't be used NSMutableAttributedString and NSAttributedString. What I've tried is:
NSMutableAttributedString * newString = [[NSMutableAttributedString alloc] initWithString:#"firstsecondthird"];
[newString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0,5)];
[newString addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(5,6)];
[newString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(11,5)];
labelNode.text = [newString string];
This doesn't work and text still has it's original color. Is there any way to do this with SKLabelNode ? Using multiple SKLabelNodes is solution, but I can't say it's elegant (or performant).
I found something that may interest you on GitHub. It's in Swift but code is very short and should be easily comprehensible.
ASAttributedLabelNode
SKLabelNode does not support NSAttributedString or NSMutableAttributedString. When you use labelNode.text = [newString string] you're taking just the text portion of the attributed string and ignoring all of the changes you made in the previous lines.
In Swift 4:
let newString = NSMutableAttributedString(string: "firstsecondthird")
newString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.red, range: NSMakeRange(0,5))
newString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.green, range: NSMakeRange(5,6))
newString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue, range: NSMakeRange(11,5))
labelNode.attributedText = newString
As of iOS 11, SKLabelNode supports NSAttributedStrings, so ASAttributedLabelNode should no longer be necessary. Your code would look like:
NSMutableAttributedString * newString = [[NSMutableAttributedString alloc] initWithString:#"firstsecondthird"];
[newString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0,5)];
[newString addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(5,6)];
[newString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(11,5)];
labelNode.attributedText = newString;

setting NSAttributed String attribute overrides substring attributes

I have created a mutable string which will look like #"testMeIn:greenColor:Different:greencolor:Colors"
NSMutableAttributedString *mutableText = [[NSMutableAttributedString alloc] initWithAttributedString:myString];
UIColor *foregroundColor = [UIColor blackColor];
NSString *key = NSForegroundColorAttributeName;
[mutableText addAttribute:key value:foregroundColor range:NSMakeRange(0, myString.length)];
When I add Attribute foregroundColor , the existing green color in substring gets overridden by the specified black color. Though I can change the code to set the green color for substring, I would like to know is there any other way of applying styles to the part of Strings which doesn't have styles without overriding the existing styles.
You can enumerate over each attribute span in the string, and only change attributes if they are not already set
NSMutableAttributedString* aString =
[[NSMutableAttributedString alloc] initWithString:#"testMeIn DIFFERENT Colors"];
[aString setAttributes:#{NSForegroundColorAttributeName:[UIColor greenColor]}
range:(NSRange){9,9}];
[aString enumerateAttributesInRange:(NSRange){0,aString.length}
options:nil
usingBlock:
^(NSDictionary* attrs, NSRange range, BOOL *stop) {
//unspecific: don't change text color if ANY attributes are set
if ([[attrs allKeys] count]==0)
[aString addAttribute:NSForegroundColorAttributeName
value:[UIColor redColor]
range:range];
//specific: don't change text color if text color attribute is already set
if (![[attrs allKeys] containsObject:NSForegroundColorAttributeName])
[aString addAttribute:NSForegroundColorAttributeName
value:[UIColor redColor]
range:range];
}];

Any way to bold part of a NSString?

Is there any way to bold only part of a string?
For example:
Approximate Distance: 120m away
Thanks!
What you could do is use an NSAttributedString.
NSString *boldFontName = [[UIFont boldSystemFontOfSize:12] fontName];
NSString *yourString = ...;
NSRange boldedRange = NSMakeRange(22, 4);
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:yourString];
[attrString beginEditing];
[attrString addAttribute:kCTFontAttributeName
value:boldFontName
range:boldedRange];
[attrString endEditing];
//draw attrString here...
Take a look at this handy dandy guide to drawing NSAttributedString objects with Core Text.
As Jacob mentioned, you probably want to use an NSAttributedString or an NSMutableAttributedString. The following is one example of how you might do this.
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"Approximate Distance: 120m away"];
NSRange selectedRange = NSMakeRange(22, 4); // 4 characters, starting at index 22
[string beginEditing];
[string addAttribute:NSFontAttributeName
value:[NSFont fontWithName:#"Helvetica-Bold" size:12.0]
range:selectedRange];
[string endEditing];
If you do not want to bother with fonts (as not every variation of font contains "Bold"), here is another way to do this. Please be aware, this is currently only available on OS X...:
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:"Approximate Distance: 120m away"];
[attrString beginEditing];
[attrString applyFontTraits:NSBoldFontMask
range:NSMakeRange(22, 4)];
[attrString endEditing];
The code above gave me crash when I created UILabel with this attributedString.
I used this code and it worked:
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:string];
NSRange boldedRange = NSMakeRange(0, 1);
UIFont *fontText = [UIFont systemFontOfSize:12]; //[UIFont fontWithName:#"Lato-Bold" size:12];
NSDictionary *dictBoldText = [NSDictionary dictionaryWithObjectsAndKeys:fontText, NSFontAttributeName, nil];
[attrString setAttributes:dictBoldText range:boldedRange];
Swift
Also includes getting the range of the string you want to embolden dynamically
let nameString = "Magoo"
let string = "Hello my name is \(nameString)"
let attributes = [NSFontAttributeName:UIFont.systemFontOfSize(14.0),NSForegroundColorAttributeName: UIColor.black]
let boldAttribute = [NSFontAttributeName:UIFont.boldSystemFontOfSize(14.0)]
let attributedString = NSMutableAttributedString(string: string, attributes: attributes)
let nsString = NSString(string: string)
let range = nsString.rangeOfString(nameString)
if range.length > 0 { attributedString.setAttributes(boldAttribute, range: range) }
someLabel.attributedText = attributedString
To bold a string without hardcoding its font, you can use the StrokeWidth attribute with a negative value:
let s = NSMutableAttributedString(string: "Approximate Distance: 120m away")
s.addAttribute(NSStrokeWidthAttributeName, value: NSNumber(value: -3.0), range: NSRange(22..<26))
An NSString is just a data container. It doesn't contain any details about presentation concerns.
It sounds like what you probably want to do is bold part of the UILabel that is being used to display your string. Which I don't think you can do. But you could always break the UI down into three labels, one for "Approximate Distance:", one for "120 m", and one for "away". Place them in-line with each other and you should get the desired effect.
Another option might be to use a UIWebView and a little bit of markup to display your string with embedded formatting information, as discussed here:
http://iphoneincubator.com/blog/windows-views/display-rich-text-using-a-uiwebview
In Xamarin ios you can bold part of a NSString this way:
public static NSMutableAttributedString BoldRangeOfString (string str, float fontSize, int startRange, int lengthRange)
{
var firstAttributes = new UIStringAttributes {
Font = UIFont.BoldSystemFontOfSize(fontSize)
};
NSMutableAttributedString boldString = new NSMutableAttributedString (str);
boldString.SetAttributes (firstAttributes.Dictionary, new NSRange (startRange, lengthRange));
return boldString;
}
and call this method:
myLabel = new UILabel ();
...
myLabel.AttributedText = BoldRangeOfString("my text", fontSize, startRange, lengthRange);
I coupled #Jacob Relkin and #Andrew Marin answers, otherwise, I got the crashes. Here is the answer for iOS9:
UIFont *boldFont = [UIFont boldSystemFontOfSize:12];
NSString *yourString = #"Approximate Distance: 120m away";
NSRange boldedRange = NSMakeRange(22, 4);
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:yourString];
[attrString beginEditing];
[attrString addAttribute:NSFontAttributeName
value:boldFont
range:boldedRange];
[attrString endEditing];
I took a look at the official documentation: 1 and 2.
Shorter way using Swift5+
let labelNotes = UILabel() //or UITextView(), etc...
let attributedNotes = NSMutableAttributedString(string: "Bold: some stuff not bold")
attributedNotes.addAttribute(NSAttributedString.Key.font, value: UIFont.boldSystemFont(ofSize: 14), range: NSRange(location: 0, length: 5))
labelNotes.attributedText = attributedNotes
If you don't want to hardcode the font or/and the size try this code for bolding full strings:
NSMutableAttributedString *myString = [[NSMutableAttributedString alloc] initWithString:mainString];
[myString beginEditing];
[myString addAttribute:NSStrokeWidthAttributeName
value:[[NSNumber alloc] initWithInt: -3.f]
range:NSMakeRange(0, [mainString length])];
[myString endEditing];