How do you use NSAttributedString?
When building attributed strings, I prefer to use the mutable subclass, just to keep things cleaner.
That being said, here's how you create a tri-color attributed string:
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"firstsecondthird"];[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0,5)];[string addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(5,6)];[string addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(11,5)];
typed in a browser. caveat implementor
Obviously you're not going to hard-code in the ranges like this. Perhaps instead you could do something like:
NSDictionary *wordToColorMapping = ....; //an NSDictionary of NSString => UIColor pairsNSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@""];for (NSString *word in wordToColorMapping) { UIColor *color = [wordToColorMapping objectForKey:word]; NSDictionary *attributes = [NSDictionary dictionaryWithObject:color forKey:NSForegroundColorAttributeName]; NSAttributedString *subString = [[NSAttributedString alloc] initWithString:word attributes:attributes]; [string appendAttributedString:subString]; [subString release];}//display string
The question is already answered... but I wanted to show how to add shadow and change the font with NSAttributedString as well, so that when people search for this topic they won't have to keep looking.
#define FONT_SIZE 20#define FONT_HELVETICA @"Helvetica-Light"#define BLACK_SHADOW [UIColor colorWithRed:40.0f/255.0f green:40.0f/255.0f blue:40.0f/255.0f alpha:0.4f]NSString*myNSString = @"This is my string.\nIt goes to a second line."; NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; paragraphStyle.alignment = NSTextAlignmentCenter; paragraphStyle.lineSpacing = FONT_SIZE/2; UIFont * labelFont = [UIFont fontWithName:FONT_HELVETICA size:FONT_SIZE]; UIColor * labelColor = [UIColor colorWithWhite:1 alpha:1]; NSShadow *shadow = [[NSShadow alloc] init]; [shadow setShadowColor : BLACK_SHADOW]; [shadow setShadowOffset : CGSizeMake (1.0, 1.0)]; [shadow setShadowBlurRadius : 1];NSAttributedString *labelText = [[NSAttributedString alloc] initWithString : myNSString attributes : @{ NSParagraphStyleAttributeName : paragraphStyle, NSKernAttributeName : @2.0, NSFontAttributeName : labelFont, NSForegroundColorAttributeName : labelColor, NSShadowAttributeName : shadow }];
Here is a Swift version...
Warning! This works for 4s.
For 5s you have to change all of the the Float values to Double values (because the compiler isn't working correctly yet)
Swift enum for font choice:
enum FontValue: Int { case FVBold = 1 , FVCondensedBlack, FVMedium, FVHelveticaNeue, FVLight, FVCondensedBold, FVLightItalic, FVUltraLightItalic, FVUltraLight, FVBoldItalic, FVItalic}
Swift array for enum access (needed because enum can't use '-'):
func helveticaFont (index:Int) -> (String) { let fontArray = [ "HelveticaNeue-Bold", "HelveticaNeue-CondensedBlack", "HelveticaNeue-Medium", "HelveticaNeue", "HelveticaNeue-Light", "HelveticaNeue-CondensedBold", "HelveticaNeue-LightItalic", "HelveticaNeue-UltraLightItalic", "HelveticaNeue-UltraLight", "HelveticaNeue-BoldItalic", "HelveticaNeue-Italic", ] return fontArray[index]}
Swift attributed text function:
func myAttributedText (myString:String, mySize: Float, myFont:FontValue) -> (NSMutableAttributedString) { let shadow = NSShadow() shadow.shadowColor = UIColor.textShadowColor() shadow.shadowOffset = CGSizeMake (1.0, 1.0) shadow.shadowBlurRadius = 1 let paragraphStyle = NSMutableParagraphStyle.alloc() paragraphStyle.lineHeightMultiple = 1 paragraphStyle.lineBreakMode = NSLineBreakMode.ByWordWrapping paragraphStyle.alignment = NSTextAlignment.Center let labelFont = UIFont(name: helveticaFont(myFont.toRaw()), size: mySize) let labelColor = UIColor.whiteColor() let myAttributes :Dictionary = [NSParagraphStyleAttributeName : paragraphStyle, NSKernAttributeName : 3, // (-1,5) NSFontAttributeName : labelFont, NSForegroundColorAttributeName : labelColor, NSShadowAttributeName : shadow] let myAttributedString = NSMutableAttributedString (string: myString, attributes:myAttributes) // add new color let secondColor = UIColor.blackColor() let stringArray = myString.componentsSeparatedByString(" ") let firstString: String? = stringArray.first let letterCount = countElements(firstString!) if firstString { myAttributedString.addAttributes([NSForegroundColorAttributeName:secondColor], range:NSMakeRange(0,letterCount)) } return myAttributedString}
first and last extension used for finding ranges in a string array:
extension Array { var last: T? { if self.isEmpty { NSLog("array crash error - please fix") return self [0] } else { return self[self.endIndex - 1] } }}extension Array { var first: T? { if self.isEmpty { NSLog("array crash error - please fix") return self [0] } else { return self [0] } }}
new colors:
extension UIColor { class func shadowColor() -> UIColor { return UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 0.3) } class func textShadowColor() -> UIColor { return UIColor(red: 50.0/255.0, green: 50.0/255.0, blue: 50.0/255.0, alpha: 0.5) } class func pastelBlueColor() -> UIColor { return UIColor(red: 176.0/255.0, green: 186.0/255.0, blue: 255.0/255.0, alpha: 1) } class func pastelYellowColor() -> UIColor { return UIColor(red: 255.0/255.0, green: 238.0/255.0, blue: 140.0/255.0, alpha: 1) }}
my macro replacement:
enum MyConstants: Float { case CornerRadius = 5.0}
my button maker w/attributed text:
func myButtonMaker (myView:UIView) -> UIButton { let myButton = UIButton.buttonWithType(.System) as UIButton myButton.backgroundColor = UIColor.pastelBlueColor() myButton.showsTouchWhenHighlighted = true; let myCGSize:CGSize = CGSizeMake(100.0, 50.0) let myFrame = CGRectMake(myView.frame.midX - myCGSize.height,myView.frame.midY - 2 * myCGSize.height,myCGSize.width,myCGSize.height) myButton.frame = myFrame let myTitle = myAttributedText("Button",20.0,FontValue.FVLight) myButton.setAttributedTitle(myTitle, forState:.Normal) myButton.layer.cornerRadius = myButton.bounds.size.width / MyConstants.CornerRadius.toRaw() myButton.setTitleColor(UIColor.whiteColor(), forState: .Normal) myButton.tag = 100 myButton.bringSubviewToFront(myView) myButton.layerGradient() myView.addSubview(myButton) return myButton}
my UIView/UILabel maker w/attributed text, shadow, and round corners:
func myLabelMaker (myView:UIView) -> UIView { let myFrame = CGRectMake(myView.frame.midX / 2 , myView.frame.midY / 2, myView.frame.width/2, myView.frame.height/2) let mylabelFrame = CGRectMake(0, 0, myView.frame.width/2, myView.frame.height/2) let myBaseView = UIView() myBaseView.frame = myFrame myBaseView.backgroundColor = UIColor.clearColor() let myLabel = UILabel() myLabel.backgroundColor=UIColor.pastelYellowColor() myLabel.frame = mylabelFrame myLabel.attributedText = myAttributedText("This is my String",20.0,FontValue.FVLight) myLabel.numberOfLines = 5 myLabel.tag = 100 myLabel.layer.cornerRadius = myLabel.bounds.size.width / MyConstants.CornerRadius.toRaw() myLabel.clipsToBounds = true myLabel.layerborders() myBaseView.addSubview(myLabel) myBaseView.layerShadow() myBaseView.layerGradient() myView.addSubview(myBaseView) return myLabel}
generic shadow add:
func viewshadow<T where T: UIView> (shadowObject: T){ let layer = shadowObject.layer let radius = shadowObject.frame.size.width / MyConstants.CornerRadius.toRaw(); layer.borderColor = UIColor.whiteColor().CGColor layer.borderWidth = 0.8 layer.cornerRadius = radius layer.shadowOpacity = 1 layer.shadowRadius = 3 layer.shadowOffset = CGSizeMake(2.0,2.0) layer.shadowColor = UIColor.shadowColor().CGColor}
view extension for view style:
extension UIView { func layerborders() { let layer = self.layer let frame = self.frame let myColor = self.backgroundColor layer.borderColor = myColor.CGColor layer.borderWidth = 10.8 layer.cornerRadius = layer.borderWidth / MyConstants.CornerRadius.toRaw() } func layerShadow() { let layer = self.layer let frame = self.frame layer.cornerRadius = layer.borderWidth / MyConstants.CornerRadius.toRaw() layer.shadowOpacity = 1 layer.shadowRadius = 3 layer.shadowOffset = CGSizeMake(2.0,2.0) layer.shadowColor = UIColor.shadowColor().CGColor } func layerGradient() { let layer = CAGradientLayer() let size = self.frame.size layer.frame.size = size layer.frame.origin = CGPointMake(0.0,0.0) layer.cornerRadius = layer.bounds.size.width / MyConstants.CornerRadius.toRaw(); var color0 = CGColorCreateGenericRGB(250.0/255, 250.0/255, 250.0/255, 0.5) var color1 = CGColorCreateGenericRGB(200.0/255, 200.0/255, 200.0/255, 0.1) var color2 = CGColorCreateGenericRGB(150.0/255, 150.0/255, 150.0/255, 0.1) var color3 = CGColorCreateGenericRGB(100.0/255, 100.0/255, 100.0/255, 0.1) var color4 = CGColorCreateGenericRGB(50.0/255, 50.0/255, 50.0/255, 0.1) var color5 = CGColorCreateGenericRGB(0.0/255, 0.0/255, 0.0/255, 0.1) var color6 = CGColorCreateGenericRGB(150.0/255, 150.0/255, 150.0/255, 0.1) layer.colors = [color0,color1,color2,color3,color4,color5,color6] self.layer.insertSublayer(layer, atIndex: 2) }}
the actual view did load function:
func buttonPress (sender:UIButton!) { NSLog("%@", "ButtonPressed")}override func viewDidLoad() { super.viewDidLoad() let myLabel = myLabelMaker(myView) let myButton = myButtonMaker(myView) myButton.addTarget(self, action: "buttonPress:", forControlEvents:UIControlEvents.TouchUpInside) viewshadow(myButton) viewshadow(myLabel)}
I think, it is a very convenient way to use regular expressions
to find a range for applying attributes. This is how I did it:
NSMutableAttributedString *goodText = [[NSMutableAttributedString alloc] initWithString:articleText];NSRange range = [articleText rangeOfString:@"\\[.+?\\]" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];if (range.location != NSNotFound) { [goodText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Georgia" size:16] range:range]; [goodText addAttribute:NSForegroundColorAttributeName value:[UIColor brownColor] range:range];}NSString *regEx = [NSString stringWithFormat:@"%@.+?\\s", [self.article.titleText substringToIndex:0]];range = [articleText rangeOfString:regEx options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];if (range.location != NSNotFound) { [goodText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Georgia-Bold" size:20] range:range]; [goodText addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:range];}[self.textView setAttributedText:goodText];
I was searching for a list of available attributes and didn't find them here and in a class reference's first page. So I decided to post here information on that.
Attributed strings support the following standard attributes for text. If the key is not in the dictionary, then use the default values described below.
NSString *NSFontAttributeName;NSString *NSParagraphStyleAttributeName;NSString *NSForegroundColorAttributeName;NSString *NSUnderlineStyleAttributeName;NSString *NSSuperscriptAttributeName;NSString *NSBackgroundColorAttributeName;NSString *NSAttachmentAttributeName;NSString *NSLigatureAttributeName;NSString *NSBaselineOffsetAttributeName;NSString *NSKernAttributeName;NSString *NSLinkAttributeName;NSString *NSStrokeWidthAttributeName;NSString *NSStrokeColorAttributeName;NSString *NSUnderlineColorAttributeName;NSString *NSStrikethroughStyleAttributeName;NSString *NSStrikethroughColorAttributeName;NSString *NSShadowAttributeName;NSString *NSObliquenessAttributeName;NSString *NSExpansionAttributeName;NSString *NSCursorAttributeName;NSString *NSToolTipAttributeName;NSString *NSMarkedClauseSegmentAttributeName;NSString *NSWritingDirectionAttributeName;NSString *NSVerticalGlyphFormAttributeName;NSString *NSTextAlternativesAttributeName;
NSAttributedString programming guide
A full class reference is here.