I have a UILabel in my storyboard, and I have an #IBOutlet to it in my controller. In my viewDidLoad, I am setting its attributed text with two different font sizes.
let str1 = NSMutableAttributedString(string: "first", attributes: [NSFontAttributeName: UIFont.systemFontOfSize(15.0)])
let str2 = NSMutableAttributedString(string: "second", attributes: [NSFontAttributeName: UIFont.systemFontOfSize(10.0)])
str1.appendAttributedString(str2)
myLabel.attributedText = str1
Unfortunately, when I run the app, I can see the "firstsecond" string, but all in the same size (str1's 15-point font). Why is str2's 10-point font not being set?
Thanks in advance.
You have to use addAttribute(...) to apply multiple attributes to the same string.
let first = "first"
let second = "second"
let string = NSMutableAttributedString(string: first + second)
string.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(15), range: NSMakeRange(0, first.characters.count))
string.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(10), range: NSMakeRange(first.characters.count, second.characters.count))
myLabel.attributedText = string
The documentation for UILabel's attributedText property states:
assigning a new value updates the values in the font, textColor, and
other style-related properties so that they reflect the style
information starting at location 0 in the attributed string.
So whatever the style information is at location 0, that's what the label's going to be. That's why you're only seeing str1's 15 point font.
See below:
// Description (HTML string):
var attrStr = NSMutableAttributedString(
data: formatted.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil,
error: nil)
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = NSTextAlignment.Justified
attrStr?.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, attrStr!.length))
attrStr?.addAttribute(NSKernAttributeName, value: -0.1, range: NSMakeRange(0, attrStr!.length))
descLabel.attributedText = attrStr
descLabel.sizeToFit()
So far all my attempts to justify text, result in the very same behavior. I've tried via inline-style CSS int eh html text, or using textAlignment property.
The issue is, justifying the text breaks the letter spacing (actually it increases the tracking/kerning of each word instead of only increasing the space in-between).
See:
I would like so see a solution that is not a hack, since I have devised some hacks of my own but then I fall into too many possibilities for other issues.
I'm I missing something simple? I've been through all the UI options and as seen in my code sample, I even tried changing the Kern, which does change but only in proportion (i.e. it still increases).
Have you tried adding a NSParagraphStyle attribute with hyphenationFactor of 1?
This will keep your kerning and word spacing, but will add hyphenation when the word needs to break to the next line.
Okay I'm having an issue with NSAttributeString. I'm getting different html/css strings that occupy "busDescriptio" depending on what business you choose from a website database. I'm able to customize the string as I see fit as NSAttributeString but unfortunately on some cases the string already has css scripting in it which overrides my style that I insert into the string. Is it possible to override the scripting that is within my string? If so, how would I be able to do this? ***If I cant override the script can I just extract a certain tag from my string or replace it? Oh here is what my string looks like. As you see their is a style that is being poppulated within the string aka(busDescriptio). I'm not able to change that with regular scripting using NSAttributeString.
/* This is a random description my busDescriptio pulls in which changes everytime someone selects a different business*/<p><span style="color:rgb(0, 0, 0); font-family:verdana,arial,tahoma; font-size:12px">Baxter Eye Care has been serving The Woodlands with quality eye care and personal friendly service since 1981. Dr. Baxter, Dr. Daniels and Dr. Shosa are dedicated to your eye health and vision needs.</span></p>
This is the code I'm using to do this
extension String {
var html2String:String {
return NSAttributedString(data: dataUsingEncoding(NSUTF8StringEncoding)!, options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding], documentAttributes: nil, error: nil)!.string
}
var html2NSAttributedString:NSAttributedString {
return NSAttributedString(data: dataUsingEncoding(NSUTF8StringEncoding)!, options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding], documentAttributes: nil, error: nil)!
}
}
extension NSData{
var htmlString:String {
return NSAttributedString(data: self, options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding], documentAttributes: nil, error: nil)!.string
}
}
let result = html2String("\(busDescriptio)") // Business Description HTML
let yourAttributedText = "<style type=\"text/css\">#busDescriptio{color:white;align-content:left;}#green{color:#0F0}#blue{color: #00F; font-weight: Bold; font-size: 32}</style><span id=\"busDescriptio\" >\(result),</span><span id=\"green\" > Green </span><span id=\"blue\">and Blue</span>".html2NSAttributedString
// Create UITextView
var textView = UITextView(frame: CGRectMake(0, 95.0, screenWidth-10, 300.0))
textView.attributedText = yourAttributedText
textView.backgroundColor = UIColor.clearColor()
textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
border.addSubview(textView)
func html2String(html:String) -> String {
return NSAttributedString(data: html.dataUsingEncoding(NSUTF8StringEncoding)!, options:[NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding], documentAttributes: nil, error: nil)!.string
}
I think you're stuck thinking about HTML. Once you have created an NSAttributedString or, more accurately, an NSMutableAttributedString from the HTML, you can just apply your own attributes. At that point, the HTML is no longer relevant. You certainly shouldn't try to achieve formatting by manipulating the HTML before converting it into an attributed string.
You don't say what "style" or attributes you want to change, so it's hard to give you an example, but just set or remove whatever attributes you want. You have the power to completely override whatever styling was introduced by the CSS in the HTML.
For example, do [someMutableAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, someMutableAttributedString.length)] to change the color to blue.
Here's some Objective-C code which works for me on OS X:
NSString* html = #"<style type=\"text/css\">#busDescriptio{color:white;align-content:left;}#green{color:#0F0}#blue{color: #00F; font-weight: Bold; font-size: 32}</style><span id=\"busDescriptio\" ><p><span style=\"color:rgb(0, 0, 0); font-family:verdana,arial,tahoma; font-size:12px\">Baxter Eye Care has been serving The Woodlands with quality eye care and personal friendly service since 1981. Dr. Baxter, Dr. Daniels and Dr. Shosa are dedicated to your eye health and vision needs.</span></p>,</span><span id=\"green\" > Green </span><span id=\"blue\">and Blue</span>";
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:data
options:#{ NSCharacterEncodingDocumentOption: #(NSUTF8StringEncoding),
NSDocumentTypeDocumentOption: NSHTMLTextDocumentType }
documentAttributes:NULL
error:NULL];
[str addAttribute:NSForegroundColorAttributeName value:[NSColor redColor] range:NSMakeRange(0, str.length)];
self.label.attributedStringValue = str;
I don't work in Swift, but here's a pass at a simple translation:
let html = "<style type=\"text/css\">#busDescriptio{color:white;align-content:left;}#green{color:#0F0}#blue{color: #00F; font-weight: Bold; font-size: 32}</style><span id=\"busDescriptio\" ><p><span style=\"color:rgb(0, 0, 0); font-family:verdana,arial,tahoma; font-size:12px\">Baxter Eye Care has been serving The Woodlands with quality eye care and personal friendly service since 1981. Dr. Baxter, Dr. Daniels and Dr. Shosa are dedicated to your eye health and vision needs.</span></p>,</span><span id=\"green\" > Green </span><span id=\"blue\">and Blue</span>"
let data = html.dataUsingEncoding(NSUTF8StringEncoding)!
var str = NSMutableAttributedString(data:data,
options:[ NSCharacterEncodingDocumentOption: NSUTF8StringEncoding,
NSDocumentTypeDocumentOption: NSHTMLTextDocumentType ],
documentAttributes:nil,
error:nil)
str.addAttribute(NSForegroundColorAttributeName, value:NSColor.redColor(), range:NSMakeRange(0, str.length))
self.label.attributedStringValue = str;
So, the code starts with HTML code in a string. It creates an NSMutableAttributedString from that. Then it changes the foreground color, effectively replacing the color resulting from the HTML. Finally, it sets the attributed string as the content of an NSTextField label. For iOS, you could use a UILabel or UITextView or whatever.
In the code you put in your question, there are some things that are troublesome. It's not clear what busDescription is. Is it a string containing HTML code?
Is there a reason that you're interpolating it into a string to pass it to html2String()? That is, why this:
let result = html2String("\(busDescriptio)")
and not this:
let result = html2String(busDescriptio)
?
It seems that html2String() interprets HTML into an attributed string and then just extracts the plain text from that. Why are you doing that?
You are then interpolating that plain text into another block of HTML code. Again, why are you doing that? If you want to apply colors, fonts, etc. to the plain text string, just build an attributed string directly from that plain text string — no HTML involved — and apply the desired attributes.
Or, start from the original busDescriptio, make a mutable attributed string from that HTML code, and apply whatever attributes you like to that.
For example, here's another example:
NSString* busDescriptio = #"<p><span style=\"color:rgb(0, 0, 0); font-family:verdana,arial,tahoma; font-size:12px\">Baxter Eye Care has been serving The Woodlands with quality eye care and personal friendly service since 1981. Dr. Baxter, Dr. Daniels and Dr. Shosa are dedicated to your eye health and vision needs.</span></p>";
NSData* data = [busDescriptio dataUsingEncoding:NSUTF8StringEncoding];
NSMutableAttributedString* foo = [[NSMutableAttributedString alloc] initWithData:data
options:#{ NSCharacterEncodingDocumentOption: #(NSUTF8StringEncoding),
NSDocumentTypeDocumentOption: NSHTMLTextDocumentType }
documentAttributes:NULL
error:NULL];
[str addAttribute:NSFontAttributeName value:[NSFont fontWithName:#"Verdana" size:12] range:NSMakeRange(0, str.length)];
Basically, although your initial input is HTML code, you build a mutable attributed string from that and then don't work in HTML from that point on. Just apply and/or remove attributes from the attributed string, as desired. You can also build separate attributed strings, like your green or blue text in your example, and append those to the mutable one.
Okay so using what Ken Thomases wrote I was able to make it work! After looking at his code and seeing what I did wrong I was able to make it work. Here it is in Swift code just in case anyone has the same problem I had. I'm still having trouble with some images but ideally everything from font color, size, height, alignments and background work! Thanks
// busDescriptio is being occupied by a string with html and css scripting
var data: NSData = busDescriptio.dataUsingEncoding(NSUTF8StringEncoding)!
var foo: NSMutableAttributedString = NSMutableAttributedString(data: data,
options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding],
documentAttributes:nil,
error:nil)!
var paragraph = NSMutableParagraphStyle()
paragraph.alignment = NSTextAlignment.Left
foo.addAttribute(NSFontAttributeName, value: UIFont(name: "Arial", size:12)!, range:NSMakeRange(0, foo.length))
foo.addAttribute(NSForegroundColorAttributeName, value: UIColor.whiteColor(), range: NSRange(location:0,length: foo.length))
foo.addAttribute(NSBackgroundColorAttributeName, value: UIColor.clearColor(), range: NSRange(location: 0, length: foo.length))
foo.addAttribute(NSParagraphStyleAttributeName, value:paragraph, range:NSMakeRange(0, foo.length))
I'm using MMMarkdown and when I apply no font to UILabel or UITextView (I have to keep UITextView so TTTAttributedLabel won't do), all of markdown works. When I give the label or textview a font, I only get markdown hyperlinks to work. I tried switching to the AttributedMarkdown library but hyperlinks don't work at all with that one.
In my Markdown Class:
class Markdown: NSObject {
func markdownString(stringForVideoDescription:NSString) -> NSMutableAttributedString {
var options = [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType]
var error:NSError?
var markdown:NSString = stringForVideoDescription
var html:NSString = MMMarkdown.HTMLStringWithMarkdown(markdown, error: &error)
var markdownAttributedString:NSMutableAttributedString = NSMutableAttributedString(data: html.dataUsingEncoding(NSUTF32StringEncoding)!, options: options, documentAttributes: nil, error: &error)!
if let font: UIFont = UIFont(name: "Marion-Regular", size: 14) {
markdownAttributedString.addAttributes([NSUnderlineStyleAttributeName:NSUnderlineStyleAttributeName], range: NSMakeRange(0, markdownAttributedString.length))
}
println(html) // used to see that markdown is in fact working
return markdownAttributedString
}
In my view, I'm actually pulling from json api but I replace to test with:
let bodyText = "\*This* sample \[I'm an inline-style link](https://www.google.com)"
myTextView.attributedText = Markdown().markdownString(bodyText)
Do I need to set up css to specify font for each bold, italicized text? Any help is greatly appreciated as my designers don't want to use the default font of Times New Roman.
I want to superscript all the instances of ® character in a block of text (legal disclaimer, naturally ;)) and the default way NSAttributedString is not very good.
If I just let the character be and only use unmodified NSString, it is rendered the same size as a capital letter and is placed approximately at the baseline. If I add the superscript attribute to NSAttributedString as follows:
[attrStr setAttributes:#{(NSString *)kCTSuperscriptAttributeName : #1} range:NSMakeRange(locationOfReg, 1)];
The character is lifted off the baseline, its size is unchanged, but the line spacing is now affected because the raised character would otherwise intrude on the line above.
To illustrate:
I created this image in Photoshop where the desired position was achieved by reducing the size of the character and shifting the baseline. I know how to change the font size in iOS, but changing the baseline seems trickier. Any suggestions on how to achieve this?
Edit: I suppose I could use the superscript attribute as a way to shift the baseline up. Now it would be great to figure out a way to get the current font size and subsequently reduce it to allow the same method to be used on blocks of text of different size.
The following code seems to do the trick:
UIFont *fnt = [UIFont fontWithName:#"Helvetica" size:20.0];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:#"GGG®GGG"
attributes:#{NSFontAttributeName: [fnt fontWithSize:20]}];
[attributedString setAttributes:#{NSFontAttributeName : [fnt fontWithSize:10]
, NSBaselineOffsetAttributeName : #10} range:NSMakeRange(3, 1)];
Swift version:
let fnt = UIFont(name:"Helvetica", size:20.0)
let attributedString = NSMutableAttributedString(string:"GGG®GGG", attributes:[NSFontAttributeName : fnt!])
attributedString.setAttributes([NSFontAttributeName : fnt!.fontWithSize(10), NSBaselineOffsetAttributeName: 10], range: NSRange(location: 3, length: 1))
Swift 5
let fnt = UIFont(name:"Helvetica", size:20.0)
let attributedString = NSMutableAttributedString(string:"2.099", attributes:[NSAttributedString.Key.font : fnt!])
attributedString.setAttributes([NSAttributedString.Key.font : fnt!.withSize(10), NSAttributedString.Key.baselineOffset: 10], range: NSRange(location: 4, length: 1))
Swift 4.2
In my example I want to subscript one instance of infinity symbol so my label's title will look like this:
let font = UIFont(name: "Helvetica", size: 14.0)
let attributedString = NSMutableAttributedString(string: "Solids(ΔE∞)•G7®", attributes: [NSAttributedStringKey.font : font!])
attributedString.setAttributes([NSAttributedStringKey.baselineOffset: -5], range: NSRange(location: 9, length: 1))
solidsLbl.attributedText = attributedString