Applying different attributes for different portions of an NSAttributedstring
Make a mutable copy of the attributed string, and then use either:
-setAttributes:range:
-addAttributes:range:
-addAttribute:value:range:
-removeAttributes:range:
for example, to set a red color for the first four letters:
NSMutableAttributedString *mutAttrStr = [attributedString mutableCopy];
CGColorRef redClr = [UIColor redColor].CGColor;
NSDictionary *newAttributes = [NSDictionary dictionaryWithObject:(id)redClr forKey:(id)kCTForegroundColorAttributeName];
[mutAttrStr addAttributes:newAttributes range:NSMakeRange(0, 4)];
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableAttributedString_Class/Reference/Reference.html
There's no built-in way to draw the background color on iOS. You can create a custom string constant for the attribute, eg "MyBackgroundColorAttributeName", and then you'll have to draw it yourself.
Using multiple styles in one attributed string
Yes, you can
var attributedText = NSMutableAttributedString()
let str1 = text1
let str2 = " (\(text2) people added)"
let attr1 = [NSAttributedStringKey.foregroundColor: UIColor.blue]
let attr2 = [NSAttributedStringKey.foregroundColor: UIColor.red]
attributedText.append(NSAttributedString(string: str1, attributes: attr1))
attributedText.append(NSAttributedString(string: str2, attributes: attr2))
cell.textLabel?.attributedText = attributedText
Swift 3 NSAttributedString multiple attributes
The main issue is that you are passing an array [attr.. , attr...]
rather than one dictionary.
You need to merge the two dictionaries into one
let attributeFontSaySomething : [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 12.0)]
let attributeColorSaySomething : [String : Any] = [NSForegroundColorAttributeName : UIColor.blue]
var attributes = attributeFontSaySomething
for (key, value) in attributeColorSaySomething {
attributes(value, forKey: key)
}
let attStringSaySomething = NSAttributedString(string: "Say something", attributes: attributes)
However it might be easier to create the dictionary literally:
let attributes : [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 12.0), NSForegroundColorAttributeName : UIColor.blue]
Apply attributes from one NSAttributedString to another
Use - (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange
to get the attributes at a specific index (e.g. 0 for the first character in string). Then use this attribute in - (void)setAttributes:(NSDictionary *)attributes range:(NSRange)aRange
to set this attributes for all characters in range.
Change attributes of substrings in a NSAttributedString
@Hyperlord's answer will work, but only if there is one occurence of the word "and" in the input string. Anyway, what I would do is use NSString's stringByReplacingOccurrencesOfString:
initially to change every "and" to an "AND", then use a little regex to detect matches in attributed string, and apply NSForegroundColorAttributeName
at that range. Here's an example:
NSString *initial = @"This is the text and i want to replace something and stuff and stuff";
NSString *text = [initial stringByReplacingOccurrencesOfString:@"and" withString:@"AND"];
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:text];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(AND)" options:kNilOptions error:nil];
NSRange range = NSMakeRange(0,text.length);
[regex enumerateMatchesInString:text options:kNilOptions range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange subStringRange = [result rangeAtIndex:1];
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:subStringRange];
}];
And finally, just apply the attributed string to your label.
[myLabel setAttributedText:mutableAttributedString];
replace entire text string in NSAttributedString without modifying other attributes
You can use NSMutableAttributedString and just update the string, the attributes won't change.
Example:
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:@"my string" attributes:@{NSForegroundColorAttributeName: [UIColor blueColor], NSFontAttributeName: [UIFont systemFontOfSize:20]}];
//update the string
[mutableAttributedString.mutableString setString:@"my new string"];
NSAttributedString, change the font overall BUT keep all other attributes?
Since rmaddy's answer did not work for me (f.fontDescriptor.withFace(font.fontName)
does not keep traits like bold), here is an updated Swift 4 version that also includes color updating:
extension NSMutableAttributedString {
func setFontFace(font: UIFont, color: UIColor? = nil) {
beginEditing()
self.enumerateAttribute(
.font,
in: NSRange(location: 0, length: self.length)
) { (value, range, stop) in
if let f = value as? UIFont,
let newFontDescriptor = f.fontDescriptor
.withFamily(font.familyName)
.withSymbolicTraits(f.fontDescriptor.symbolicTraits) {
let newFont = UIFont(
descriptor: newFontDescriptor,
size: font.pointSize
)
removeAttribute(.font, range: range)
addAttribute(.font, value: newFont, range: range)
if let color = color {
removeAttribute(
.foregroundColor,
range: range
)
addAttribute(
.foregroundColor,
value: color,
range: range
)
}
}
}
endEditing()
}
}
Or, if your mix-of-attributes does not include font,
then you don't need to remove old font:
let myFont: UIFont = .systemFont(ofSize: UIFont.systemFontSize);
myAttributedText.addAttributes(
[NSAttributedString.Key.font: myFont],
range: NSRange(location: 0, length: myAttributedText.string.count));
Notes
The problem with f.fontDescriptor.withFace(font.fontName)
is that it removes symbolic traits like italic
, bold
or compressed
, since it will for some reason override those with default traits of that font face. Why this is so totally eludes me, it might even be an oversight on Apple's part; or it's "not a bug, but a feature", because we get the new font's traits for free.
So what we have to do is create a font descriptor that has the symbolic traits from the original font's font descriptor: .withSymbolicTraits(f.fontDescriptor.symbolicTraits)
. Props to rmaddy for the initial code on which I iterated.
I've already shipped this in a production app where we parse a HTML string via NSAttributedString.DocumentType.html
and then change the font and color via the extension above. No problems so far.
Example of NSAttributedString with two different font sizes?
You would do something like this…
NSMutableAttributedString *hogan = [[NSMutableAttributedString alloc] initWithString:@"Presenting the great... Hulk Hogan!"];
[hogan addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:20.0]
range:NSMakeRange(24, 11)];
This will set the last two words in 20-point text; the rest of the string will use the default value (which I believe is 12 points). The thing that might be confusing about setting the text size is that you have to set the typeface and the size at the same time—each UIFont
object encapsulates both of those properties.
Related Topics
How to Connect Xcode 9 and Github
How to Check a App Was Installed or Updated
Nscoding Required Initializer in Inherited Classes in Swift
How to Compile Aws Customidentityprovider on Xcode 8 Beta 6
Cannot Call Value of Non-Function Type 'Module<Firebase>'
Ar Refernce Image Plane Was Not Position Properly in iOS Swift
Force Wkwebview to Show Mobile Version
Receive Signal from Beacon While App Is in the Background
How to Get System Device Language, Swift iOS
Ad-Hoc Distributed Application Failed to Launch in Time
iPhone /iOS: Presenting HTML 5 Keyboard for Postal Codes
How to Detect If The HTML5 Autoplay Attribute Is Supported
Allow Zooming Within Iframe But Not on Page in iOS
Adding an Skscene to a Uiviewcontroller
Revealviewcontroller() Always Returns Nil
Detecting Double and Single Tap on Uitableviewcell to Perform Two Action