NSAttributeString battling css scripting already in a string - ios

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))

Related

Justify text in iOS (Swift or Objective-C) without breaking tracking/kerning/letter spacing

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.

Calling a NSBaseURLDocumentOption for NSMutableAttributedString IOS Swift

I'm using a NSMutableAttributedString to pull in business descriptions that are in html and css scripting. Although I'm able to manipulate the font color, style, and ext the internal images arent showing up. It works for <img alt='' src='http://i189.photobucket.com/albums/z189/txmom3x/Crabby%20Daddy/crabbydrinks.jpg' style='height:148px; width:222px' />
but not this
<img alt='' src='/cnt/files/This%20is%20the%20one/pinterest.png' style='height:28px; width:28px' />
The URL for the image is presumably relative to the URL of the page the HTML is considered to come from. By the time you create the attributed string from the HTML, it's no longer associated with a URL, so the system can't construct an absolute URL for the image and can't find it. How can I specify the base URL? I heard you could use something like NSBaseURLDocumentOption but that only works for OS X
// Create NSMutableAttributedString
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))

NSAttributedString Font Change While Retaining Format Attributes

I have an existing NSAttributedString that may include user edited attributes (i.e. Bold, Italic, Underline). I need to be able to change the base font from say Georgia to Helvetica, while maintaining the format attributes. Setting the font like so, overrides all format attributes (i.e. Georgia-Bold):
NSDictionary *fontFaceStyle = [[NSDictionary alloc] init];
fontFaceStyle = #{NSFontAttributeName:[UIFont fontWithName:#"Helvetica" size:12.0]};
[combinedAttributedTextString setAttributes:fontFaceStyle range:NSMakeRange(0, combinedAttributedTextString.length)];
I have seen some different but related threads that suggest enumerating over each attribute span in the string, make font change, then reapply the applicable format attributes. This seems pretty intensive, and I'm not sure how you'd apply it when multiple attributes could be present (i.e. Bold AND Italic AND Underline).
Thanks for any suggestions.
I had the exact same issue, and enumerating works well. Here "range" is the range you want to work on, and "newFamily" is a font-family. I am using textStorage, which I assume is doing useful cleanup for these types of edits when wrapping with calls to beginEditing() / endEditing().
textStorage.beginEditing()
textStorage.enumerateAttributes(in: range, options: [], using: { attr, attrRange, _ in
if let font = attr[NSFontAttributeName] as? NSFont {
let newFont = NSFontManager.shared().convert(font, toFamily: newFamily)
storage.addAttribute(NSFontAttributeName, value: newFont, range: attrRange)
}
})
textStorage.endEditing()

How to make particular word colored : coming from localized string file of iOS

I would like to make particular word colored like this :
Text in English :-
Same place text in French :-
These text are came from localized string file. And I found solution with the string range property NSMakeRange(0,3) for attributed text from here. This I don't want since text will differ for Localized language string. FYI, I placed text in UITextView.
So how can I achieve this by simple way? Is there any solution that can fix problem in Localized string file itself.
I know this answer is a few years late, but I solved the problem by allowing HTML in my localizable strings. For example:
"emergency" = "This is a test of the <span style=\"color:red\">emergency</span> broadcast system.";
Of course, this alone won't solve the problem, because the localizable string files in IOS don't natively support any sort of HTML/XML annotation. So, you need to process the HTML yourself when you extract the string in your swift code. Here is how I did it:
// Get the localized string
let localized = NSLocalizedString("emergency", comment: "")
// Prepend an html header to the string select the primary font and color.
// The settings on the UITextView or UILabel will be ignored, so you need to do this
let htmlString = "<span style=\"color:yellow;font-size:24px;font-family:-apple-system\">" + localized
// perform the magic to turn the html string into an NSAttributedString
let data = NSString(string: htmlString).data(using: String.Encoding.utf8.rawValue)
do {
let attributedString = try NSAttributedString(data: data!,
options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
// Show the NSAttributedString in your help UITextView (or anywhere else)
help.attributedText = attributedString
} catch {
print("There was a problem")
}
This example gives you yellow, 24-point, apple system font text. The localizable string marked the word "emergency" as red, and it will be displayed in red in the final display.
Below snippet might be used for achieving yourrequirement
UITextView *textView = //YourTextView
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithAttributedString: textView.attributedText];
[attributedText addAttribute:NSForegroundColorAttributeName
value:[UIColor redColor]
range:[textView.text rangeOfString:blueString]];
[attributedText addAttribute:NSForegroundColorAttributeName
value:[UIColor blueColor]
range:[textView.text rangeOfString:redString]];
[textView setAttributedText: attributedText];

Display html text in uitextview

How can I display HTML text in textview?
For example,
string <h1>Krupal testing <span style="font-weight:
bold;">Customer WYWO</span></h1>
Suppose text is bold so it display in textview as bold string
but I want display normal text. Is this possible in the iPhone SDK?
Use following block of code for ios 7+.
NSString *htmlString = #"<h1>Header</h1><h2>Subheader</h2><p>Some <em>text</em></p><img src='http://blogs.babble.com/famecrawler/files/2010/11/mickey_mouse-1097.jpg' width=70 height=100 />";
NSAttributedString *attributedString = [[NSAttributedString alloc]
initWithData: [htmlString dataUsingEncoding:NSUnicodeStringEncoding]
options: #{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
documentAttributes: nil
error: nil
];
textView.attributedText = attributedString;
For Swift 4, Swift 4.2: and Swift 5
let htmlString = """
<html>
<head>
<style>
body {
background-color : rgb(230, 230, 230);
font-family : 'Arial';
text-decoration : none;
}
</style>
</head>
<body>
<h1>A title</h1>
<p>A paragraph</p>
<b>bold text</b>
</body>
</html>
"""
let htmlData = NSString(string: htmlString).data(using: String.Encoding.unicode.rawValue)
let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html]
let attributedString = try! NSAttributedString(data: htmlData!, options: options, documentAttributes: nil)
textView.attributedText = attributedString
For Swift 3:
let htmlString = """
<html>
<head>
<style>
body {
background-color : rgb(230, 230, 230);
font-family : 'Arial';
text-decoration : none;
}
</style>
</head>
<body>
<h1>A title</h1>
<p>A paragraph</p>
<b>bold text</b>
</body>
</html>
"""
let htmlData = NSString(string: htmlString).data(using: String.Encoding.unicode.rawValue)
let attributedString = try! NSAttributedString(data: htmlData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
textView.attributedText = attributedString
Use a UIWebView on iOS 5-.
On iOS 6+ you can use UITextView.attributedString, see https://stackoverflow.com/a/20996085 for how.
There's also an undocumented -[UITextView setContentToHTMLString:] method. Do not use this if you want to submit to AppStore.
BHUPI's answer is correct, but if you would like to combine your custom font from UILabel or UITextView with HTML content, you need to correct your html a bit:
NSString *htmlString = #"<b>Bold</b><br><i>Italic</i><p> <del>Deleted</del><p>List<ul><li>Coffee</li><li type='square'>Tea</li></ul><br><a href='URL'>Link </a>";
htmlString = [htmlString stringByAppendingString:#"<style>body{font-family:'YOUR_FONT_HERE'; font-size:'SIZE';}</style>"];
/*Example:
htmlString = [htmlString stringByAppendingString:[NSString stringWithFormat:#"<style>body{font-family: '%#'; font-size:%fpx;}</style>",_myLabel.font.fontName,_myLabel.font.pointSize]];
*/
NSAttributedString *attributedString = [[NSAttributedString alloc]
initWithData: [htmlString dataUsingEncoding:NSUnicodeStringEncoding]
options: #{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
documentAttributes: nil
error: nil
];
textView.attributedText = attributedString;
You can see the difference on the picture below:
You can have a look the OHAttributedLabel classes, I used these to overcome this kind of problem with my textField. In this they have overridden the drawRect method to obtain the required style.
https://github.com/AliSoftware/OHAttributedLabel
My first response was made before iOS 7 introduced explicit support for displaying attributed strings in common controls. You may now set attributedText of UITextView to an NSAttributedString created from HTML content using:
-(id)initWithData:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict error:(NSError **)error
- initWithData:options:documentAttributes:error: (Apple Doc)
Original answer, preserved for history:
Unless you use a UIWebView, your solution will rely directly on CoreText. As ElanthiraiyanS points out, some open source projects have emerged to simplify rich text rendering. I would recommend NSAttributedString-Additions-For-HTML (Edit: the project has been supplanted DTCoreText), which features classes to generate and display attributed strings from HTML.
Answer has fitted to me that from BHUPI.
The code transfer to swift as below:
Pay attention "allowLossyConversion: false"
if you set the value to true, it will show pure text.
let theString = "<h1>H1 title</h1><b>Logo</b><img src='http://www.aver.com/Images/Shared/logo-color.png'><br>~end~"
let theAttributedString = try! NSAttributedString(data: theString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
UITextView_Message.attributedText = theAttributedString
For Swift3
let theString = "<h1>H1 title</h1><b>Logo</b><img src='http://www.aver.com/Images/Shared/logo-color.png'><br>~end~"
let theAttributedString = try! NSAttributedString(data: theString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
UITextView_Message.attributedText = theAttributedString
For some cases UIWebView is a good solution. Because:
it displays tables, images, other files
it's fast (comparing with NSAttributedString: NSHTMLTextDocumentType)
it's out of the box
Using NSAttributedString can lead to crashes, if html is complex or contains tables (so example)
For loading text to web view you can use the following snippet (just example):
func loadHTMLText(_ text: String?, font: UIFont) {
let fontSize = font.pointSize * UIScreen.screens[0].scale
let html = """
<html><body><span style=\"font-family: \(font.fontName); font-size: \(fontSize)\; color: #112233">\(text ?? "")</span></body></html>
"""
self.loadHTMLString(html, baseURL: nil)
}
You can also use one more way. Three20 library offers a method through which we can construct a styled textView. You can get the library here: http://github.com/facebook/three20/
The class TTStyledTextLabel has a method called textFromXHTML: I guess this would serve the purpose. But it would be possible in readonly mode. I don't think it will allow to write or edit HTML content.
There is also a question which can help you regarding this: HTML String content for UILabel and TextView
I hope its helpful.
NSDoc save the text file in a string to an html file then simultaneously load it into a webview that is in the same place as your UITextView..

Resources