Change NSAttributedString html links color - ios

Trying to display a ASTextNode (same as UILabel from AsyncDisplayKit) to display an html text. I simply have to set the label's attributed text.
There is how i work my string :
Using this extension i transform the HTML text into a NSAttributedString :
extension String {
var html2AttributedString: NSAttributedString? {
guard let data = data(using: .utf8) else { return nil }
do {
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch let error as NSError {
print(error.localizedDescription)
return nil
}
}
var html2String: String {
return html2AttributedString?.string ?? ""
}
}
Then i set my label details :
self.displayContent = NSMutableAttributedString(attributedString: content.html2AttributedString!)
self.displayContent?.addAttribute(NSFontAttributeName, value: UIFont.fontMainFeedContentFont(), range: NSRange.init(location: 0, length: self.displayContent!.length))
So i have my label with my font and it's ok, problem is that i can't change the links colors of my label, it's a system blue that i do want.
Any idea how can i change the links' colors ?
Thanks.

I found an answer for this in Swift 4.0
termsAndPolicyTextView.linkTextAttributes = [
NSAttributedString.Key.foregroundColor: UIColor.red
]
Full code:
Note: I can't set multiple color in a single textView.
let attributedString = NSMutableAttributedString(string: termsAndPolicyText)
attributedString.addAttribute(
NSAttributedString.Key.link,
value: "https://google.co.in",
range: (termsAndPolicyText as NSString).range(of: "Terms or service")
)
attributedString.addAttribute(
NSAttributedString.Key.link,
value: "https://google.co.in", // Todo set our terms and policy link here
range: (termsAndPolicyText as NSString).range(of: "Privacy & Legal Policy")
)
attributedString.addAttributes(
[NSAttributedString.Key.foregroundColor: UIColor.NMSTextColor(with: 0.6)],
range: NSRange(location: 0, length: termsAndPolicyText.count)
)
termsAndPolicyTextView.linkTextAttributes = [
NSAttributedString.Key.foregroundColor: UIColor.termstextViewTextColor()
]
termsAndPolicyTextView.attributedText = attributedString
termsAndPolicyTextView.textAlignment = .center

Ok guys, i found an ugly way to do this.
After transforming the html text to an NSMutableAttributedString, i simply loop thought all attributes, when i see an "NSLink" attribute i simply add an attribute for the attribute's range :
self.myString!.enumerateAttributes(in: NSRange(0..<myString!.length), options: []) { (attributes, range, _) -> Void in
for (attribute, object) in attributes {
if attribute == "NSLink" {
print("Attribute = \(attribute) -- \(object)")
self.myString?.addAttribute(NSForegroundColorAttributeName, value: StyleKit.color_blue_bright, range: range)
self.myString?.addAttribute(NSUnderlineColorAttributeName, value: UIColor.clear, range: range)
}
}
}

The link color can be changed in the following way. The below example will demonstrate that:
let attributedText:NSMutableAttributedString = NSMutableAttributedString(string: "why?")
attributedText.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle, range: NSMakeRange(0, attributedText.length))
attributedText.addAttribute(NSUnderlineColorAttributeName, value: UIColor.black, range: NSMakeRange(0, attributedText.length))
attributedText.addAttribute(NSForegroundColorAttributeName, value: UIColor.black, range: NSMakeRange(0, attributedText.length))
Also, you have to make the following changes to the UITextView that's displaying it.
textView.linkTextAttributes = [NSForegroundColorAttributeName : UIColor.black]
If you do not want to use UITextView to do this, you can simply use TTTAttributedLabel. It has linkAttributes and activeLinkAttributes property which you can use to achieve the desired behaviour without using UITextView.
Please let me know if it works or not. Feel free to suggest edits to make this better :)

Links color is default of attributed string. Can be specified by using css.
extension String {
var html2AttributedString: NSAttributedString? {
let html = """
<style type="text/css">
a, a:link, a:visited {
color: inherit !important;
}
</style>
""" + self
guard let data = html.data(using: .utf8) else { return nil }
...

Related

After adding a link modifier in attributeText, the text in the range becomes invisible

I’m trying to create links using NSAttributedString, but when I add a link to the text, they become invisible, underlined still visible, and if I click on the text, it will show my debug message,
let attributedText = NSMutableAttributedString(string: "Sample text for test")
attributedText.addAttribute(.link, value: "some url", range: NSMakeRange(0, 5))
attributedText.addAttribute(.underlineStyle, value: 1, range: NSMakeRange(0, 5))
attributedText.addAttribute(.foregroundColor, value: UIColor.blue, range:NSMakeRange(0, 5))
attributedText.addAttribute(.underlineColor, value: UIColor.blue, range: NSMakeRange(0, 5))
myTextView.attributedText = attributedText
myTextView.delegate = self
myTextView.isEditable = false
myTextView.isSelectable = true
I have such results
http://prnt.sc/ojd06v
You have to set the linkTextAttributes of your TextView.
This is my code example:
let data = Data("My URL stringggg...".utf8)
if let attributedString = try? NSMutableAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
let foundRange = (attributedString.string as NSString).range(of: "My URL")
if foundRange.location != NSNotFound {
attributedString.addAttribute(NSAttributedString.Key.link, value: "url", range: foundRange)
}
myTextView.linkTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
myTextView.attributedText = attributedString
}
It works for me.

How to set custom font with regular and bold font while setting html string to label in swift 4?

I am getting HTML formatted string from API response, so I need to set it to a label while maintaining Custom Font(as of my App) and also applying a style(bold, regular, etc.) to the label.
I have used an extension that enables to convert the HTML string to regular string with newlines etc. but, I was able to set font here, but only one font and it shows in regular font only, so the whole label is in one font, what I want is to set bold font to the bold HTML part and regular to regular HTML part/tag.
extension String {
var htmlToAttributedString: NSAttributedString {
guard let data = data(using: .utf8) else { return NSAttributedString() }
do {
return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch {
return NSAttributedString()
}
}
var htmlToString: String {
return htmlToAttributedString.string
}
}
//set converted html to string here
let whyAttendAttributedText: NSMutableAttributedString = NSMutableAttributedString(attributedString: attendData.whyAttendData?.desc?.htmlToAttributedString ?? NSAttributedString())
//set font here
whyAttendAttributedText.addAttributes([NSMutableAttributedString.Key.font: CommonSettings.shared.getFont(type: .regular, size: descriptionLabel.font.pointSize), NSMutableAttributedString.Key.foregroundColor: UIColor.white], range: NSMakeRange(0, whyAttendAttributedText.length))
I want to set bold and regular to the text, but as I have set only one font I was unable to get the result, is there any way to set the bold and regular font as in HTML string?
This should help :
extension String {
func attributedString(withRegularFont regularFont: UIFont, andBoldFont boldFont: UIFont) -> NSMutableAttributedString {
var attributedString = NSMutableAttributedString()
guard let data = self.data(using: .utf8) else { return NSMutableAttributedString() }
do {
attributedString = try NSMutableAttributedString(data: data,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)
let range = NSRange(location: 0, length: attributedString.length)
attributedString.enumerateAttribute(NSAttributedString.Key.font, in: range, options: .longestEffectiveRangeNotRequired) { value, range, _ in
let currentFont: UIFont = value as! UIFont
var replacementFont: UIFont? = nil
if currentFont.fontName.contains("bold") || currentFont.fontName.contains("Bold") {
replacementFont = boldFont
} else {
replacementFont = regularFont
}
let replacementAttribute = [NSAttributedString.Key.font:replacementFont!]
attributedString.addAttributes(replacementAttribute, range: range)
}
} catch let e {
print(e.localizedDescription)
}
return attributedString
}
}
Let's assume your string after parsing HTML string is: "This is your HTML string"
To create an attributed string,
let attrStr = NSMutableAttributedString(string: "This is your HTML string")
Adding UIFont attribute with value as System-Regular,
attrStr.addAttribute(.font, value: UIFont.systemFont(ofSize: 14.0, weight: .regular), range: NSRange(location: 0, length: attrStr.length))
Whenever adding an attribute to the attributed string, we need to provide the range of string in which we want to reflect the attribute.
Since we need the whole string to have Regular font, so the range is calculated as the whole string length.
Now, adding UIFont attribute with value as System-Bold to a part of the string, let's say we make HTML as bold,
attrStr.addAttribute(.font, value: UIFont.systemFont(ofSize: 14.0, weight: .bold), range: (attrStr.string as NSString).range(of: "HTML"))
We've calculated the range of HTML word within the whole string.
Similarly, you can add any of the attributes to the string giving the relevant range values.
Output: This is yourHTMLstring
Edit-1:
To calculate the range of <b> to </b> you need to calculate it manually.
Example:
let str = "This <b>is your HTML</b> string"
let range1 = (str as NSString).range(of: "<b>")
let range2 = (str as NSString).range(of: "</b>")
let requiredRange = NSRange(location: range1.location, length: range2.location + range2.length - range1.location)
The above example will work for single instance of <b>/</b> in the string.
Edit-2:
When string includes multiple instances of <b>/</b>:
let htmlStr = "This is an <b>HTML</b> parsed <b>string</b>"
let arr = htmlStr.components(separatedBy: "</b>")
let attrStr = NSMutableAttributedString()
for str in arr {
if !str.isEmpty {
let range1 = (str as NSString).range(of: "<b>")
let requiredRange = NSRange(location: range1.location, length: str.count - range1.location)
let formattedStr = NSMutableAttributedString(string: str)
formattedStr.addAttribute(.font, value: UIFont.systemFont(ofSize: 14.0, weight: .bold), range: requiredRange)
attrStr.append(formattedStr)
attrStr.append(NSAttributedString.init(string: "</b>", attributes: [.font : UIFont.systemFont(ofSize: 14.0, weight: .bold)]))
}
}
self.label.attributedText = attrStr
Output: This is an<b>HTML</b>parsed<b>string</b>
Applying Bold and other different styles to the text can be done using below method.
extension String {
func attributedString(with style: [NSAttributedString.Key: Any]? = nil,
and highlightedText: String,
with highlightedTextStyle: [NSAttributedString.Key: Any]? = nil) -> NSAttributedString {
let formattedString = NSMutableAttributedString(string: self, attributes: style)
let highlightedTextRange: NSRange = (self as NSString).range(of: highlightedText as String)
formattedString.setAttributes(highlightedTextStyle, range: highlightedTextRange)
return formattedString
}
}
Input: "This is a test message"
Expectd Output: "This is a test message"
This can be achieved as follows.
let sampleInput = "This is a test message"
let boldtext = "test"
let output = sampleInput.attributedString(with: [.font: UIFont.systemFont(ofSize: 12.0, weight: .regular)],
and: boldtext, with: UIFont.systemFont(ofSize: 12.0, weight: .bold))
Different styles can be applied using different attribute keys. Hope this helps.

NSAttributedString adding a custom attribute loses html tags [duplicate]

Say I have an NSMutableAttributedString .
The string has a varied mix of formatting throughout:
Here is an example:
This string is hell to change in iOS, it really sucks.
However, the font per se is not the font you want.
I want to:
for each and every character, change that character to a specific font (say, Avenir)
BUT,
for each and every character, keep the mix of other attributions (bold, italic, colors, etc etc) which was previously in place on that character.
How the hell do you do this?
Note:
if you trivially add an attribute "Avenir" over the whole range: it simply deletes all the other attribute ranges, you lose all formatting. Unfortunately, attributes are not, in fact "additive".
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.
Here is a much simpler implementation that keeps all attributes in place, including all font attributes except it allows you to change the font face.
Note that this only makes use of the font face (name) of the passed in font. The size is kept from the existing font. If you want to also change all of the existing font sizes to the new size, change f.pointSize to font.pointSize.
extension NSMutableAttributedString {
func replaceFont(with font: UIFont) {
beginEditing()
self.enumerateAttribute(.font, in: NSRange(location: 0, length: self.length)) { (value, range, stop) in
if let f = value as? UIFont {
let ufd = f.fontDescriptor.withFamily(font.familyName).withSymbolicTraits(f.fontDescriptor.symbolicTraits)!
let newFont = UIFont(descriptor: ufd, size: f.pointSize)
removeAttribute(.font, range: range)
addAttribute(.font, value: newFont, range: range)
}
}
endEditing()
}
}
And to use it:
let someMutableAttributedString = ... // some attributed string with some font face you want to change
someMutableAttributedString.replaceFont(with: UIFont.systemFont(ofSize: 12))
my two cents for OSX/AppKit>
extension NSAttributedString {
// replacing font to all:
func setFont(_ font: NSFont, range: NSRange? = nil)-> NSAttributedString {
let mas = NSMutableAttributedString(attributedString: self)
let range = range ?? NSMakeRange(0, self.length)
mas.addAttributes([.font: font], range: range)
return NSAttributedString(attributedString: mas)
}
// keeping font, but change size:
func setFont(size: CGFloat, range: NSRange? = nil)-> NSAttributedString {
let mas = NSMutableAttributedString(attributedString: self)
let range = range ?? NSMakeRange(0, self.length)
mas.enumerateAttribute(.font, in: range) { value, range, stop in
if let font = value as? NSFont {
let name = font.fontName
let newFont = NSFont(name: name, size: size)
mas.addAttributes([.font: newFont!], range: range)
}
}
return NSAttributedString(attributedString: mas)
}
Important -
rmaddy has invented an entirely new technique for this annoying problem in iOS.
The answer by manmal is the final perfected version.
Purely for the historical record here is roughly how you'd go about doing it the old days...
// carefully convert to "our" font - "re-doing" any other formatting.
// change each section BY HAND. total PITA.
func fixFontsInAttributedStringForUseInApp() {
cachedAttributedString?.beginEditing()
let rangeAll = NSRange(location: 0, length: cachedAttributedString!.length)
var boldRanges: [NSRange] = []
var italicRanges: [NSRange] = []
var boldANDItalicRanges: [NSRange] = [] // WTF right ?!
cachedAttributedString?.enumerateAttribute(
NSFontAttributeName,
in: rangeAll,
options: .longestEffectiveRangeNotRequired)
{ value, range, stop in
if let font = value as? UIFont {
let bb: Bool = font.fontDescriptor.symbolicTraits.contains(.traitBold)
let ii: Bool = font.fontDescriptor.symbolicTraits.contains(.traitItalic)
// you have to carefully handle the "both" case.........
if bb && ii {
boldANDItalicRanges.append(range)
}
if bb && !ii {
boldRanges.append(range)
}
if ii && !bb {
italicRanges.append(range)
}
}
}
cachedAttributedString!.setAttributes([NSFontAttributeName: font_f], range: rangeAll)
for r in boldANDItalicRanges {
cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fBOTH, range: r)
}
for r in boldRanges {
cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fb, range: r)
}
for r in italicRanges {
cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fi, range: r)
}
cachedAttributedString?.endEditing()
}
.
Footnote. Just for clarity on a related point. This sort of thing inevitably starts as a HTML string. Here's a note on how to convert a string that is html to an NSattributedString .... you will end up with nice attribute ranges (italic, bold etc) BUT the fonts will be fonts you don't want.
fileprivate extension String {
func htmlAttributedString() -> NSAttributedString? {
guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
guard let html = try? NSMutableAttributedString(
data: data,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil) else { return nil }
return html
}
}
.
Even that part of the job is non-trivial, it takes some time to process. In practice you have to background it to avoid flicker.
Obj-C version of #manmal's answer
#implementation NSMutableAttributedString (Additions)
- (void)setFontFaceWithFont:(UIFont *)font color:(UIColor *)color {
[self beginEditing];
[self enumerateAttribute:NSFontAttributeName
inRange:NSMakeRange(0, self.length)
options:0
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
UIFont *oldFont = (UIFont *)value;
UIFontDescriptor *newFontDescriptor = [[oldFont.fontDescriptor fontDescriptorWithFamily:font.familyName] fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits];
UIFont *newFont = [UIFont fontWithDescriptor:newFontDescriptor size:font.pointSize];
if (newFont) {
[self removeAttribute:NSFontAttributeName range:range];
[self addAttribute:NSFontAttributeName value:newFont range:range];
}
if (color) {
[self removeAttribute:NSForegroundColorAttributeName range:range];
[self addAttribute:NSForegroundColorAttributeName value:newFont range:range];
}
}];
[self endEditing];
}
#end
There is a tiny bug in the accepted answer causing the original font size to get lost.
To fix this simply replace
font.pointSize
with
f.pointSize
This ensures that e.g. H1 and H2 headings have the correct size.
Swift 5
This properly scales the font size, as other answers overwrite the font size, which may differ, like with sub, sup attribute
For iOS replace NSFont->UIFont and NSColor->UIColor
extension NSMutableAttributedString {
func setFont(_ font: NSFont, textColor: NSColor? = nil) {
guard let fontFamilyName = font.familyName else {
return
}
beginEditing()
enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, length), options: []) { (value, range, stop) in
if let oldFont = value as? NSFont {
let descriptor = oldFont
.fontDescriptor
.withFamily(fontFamilyName)
.withSymbolicTraits(oldFont.fontDescriptor.symbolicTraits)
// Default font is always Helvetica 12
// See: https://developer.apple.com/documentation/foundation/nsattributedstring?language=objc
let size = font.pointSize * (oldFont.pointSize / 12)
let newFont = NSFont(descriptor: descriptor, size: size) ?? oldFont
addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
}
}
if let textColor = textColor {
addAttributes([NSAttributedString.Key.foregroundColor:textColor], range: NSRange(location: 0, length: length))
}
endEditing()
}
}
extension NSAttributedString {
func settingFont(_ font: NSFont, textColor: NSColor? = nil) -> NSAttributedString {
let ms = NSMutableAttributedString(attributedString: self)
ms.setFont(font, textColor: textColor)
return ms
}
}
Would it be valid to let a UITextField do the work?
Like this, given attributedString and newfont:
let textField = UITextField()
textField.attributedText = attributedString
textField.font = newFont
let resultAttributedString = textField.attributedText
Sorry, I was wrong, it keeps the "Character Attributes" like NSForegroundColorAttributeName, e.g. the colour, but not the UIFontDescriptorSymbolicTraits, which describe bold, italic, condensed, etc.
Those belong to the font and not the "Character Attributes". So if you change the font, you are changing the traits as well. Sorry, but my proposed solution does not work. The target font needs to have all traits available as the original font for this to work.

Swift 3 render textview using NSAttributedString but want change the default font

First wanna say thank you before if this can be solved..so i have textview that get the data with html tag..so i use attributedText and function to render html..it worked..but i need to change the font family..right now by default it "times new roman" i want to change to "Helvetica" any clue? i use the extension for this :
extension Data {
var attributedString: NSAttributedString? {
do {
return try NSAttributedString(data: self, options:[NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch {
print(error)
}
return nil
}}
extension String {
var data: Data {
return Data(utf8)
}}
then i use it with :
cell.txtview.attributedText = contentText.data.attributedString
it worked but the font default become "times new roman" i need to change it..any idea? i am very appreciate it before..thank you!
enter image description here
I also already try this...see image below
enter image description here
Your attributed string must be like below :
using this link
let data = "vini".data(using: .utf8)
let attributedString = data!.attributedString
let newAttributedString = NSMutableAttributedString(attributedString: (attributedString)!)
newAttributedString.enumerateAttribute(NSFontAttributeName, in: NSMakeRange(0, (newAttributedString.length)), options: []) { value, range, stop in
guard let currentFont = value as? UIFont else {
return
}
let fontDescriptor = currentFont.fontDescriptor.addingAttributes([UIFontDescriptorFamilyAttribute: "Helvetica"])
if let newFontDescriptor = fontDescriptor.matchingFontDescriptors(withMandatoryKeys: [UIFontDescriptorFamilyAttribute]).first {
let newFont = UIFont(descriptor: newFontDescriptor, size: currentFont.pointSize)
newAttributedString.addAttributes([NSFontAttributeName: newFont], range: range)
}
}
print(newAttributedString)

Color up text in textview within a specific range [duplicate]

The issue I am having is that I want to be able to change the textColor of certain text in a TextView. I am using a concatenated string, and just want the strings I am appending into the TextView's text. It appears that what I want to use is NSMutableAttributedString, but I am not finding any resources of how to use this in Swift. What I have so far is something like this:
let string = "A \(stringOne) with \(stringTwo)"
var attributedString = NSMutableAttributedString(string: string)
textView.attributedText = attributedString
From here I know I need to find the range of words that need to have their textColor changed and then add them to the attributed string. What I need to know is how to find the correct strings from the attributedString, and then change their textColor.
Since I have too low of a rating I can't answer my own question, but here is the answer I found
I found my own answer by translating from translating some code from
Change attributes of substrings in a NSAttributedString
Here is the example of implementation in Swift:
let string = "A \(stringOne) and \(stringTwo)"
var attributedString = NSMutableAttributedString(string:string)
let stringOneRegex = NSRegularExpression(pattern: nameString, options: nil, error: nil)
let stringOneMatches = stringOneRegex.matchesInString(longString, options: nil, range: NSMakeRange(0, attributedString.length))
for stringOneMatch in stringOneMatches {
let wordRange = stringOneMatch.rangeAtIndex(0)
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.nameColor(), range: wordRange)
}
textView.attributedText = attributedString
Since I am wanting to change the textColor of multiple Strings I will make a helper function to handle this, but this works for changing the textColor.
let mainString = "Hello World"
let stringToColor = "World"
SWIFT 5
let range = (mainString as NSString).range(of: stringToColor)
let mutableAttributedString = NSMutableAttributedString.init(string: mainString)
mutableAttributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red, range: range)
textField = UITextField.init(frame: CGRect(x:10, y:20, width:100, height: 100))
textField.attributedText = mutableAttributedString
SWIFT 4.2
let range = (mainString as NSString).range(of: stringToColor)
let mutableAttributedString = NSMutableAttributedString.init(string: mainString)
mutableAttributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.red, range: range)
textField = UITextField.init(frame: CGRect(x:10, y:20, width:100, height: 100))
textField.attributedText = mutableAttributedString
I see you have answered the question somewhat, but to provide a slightly more concise way without using regex to answer to the title question:
To change the colour of a length of text you need to know the start and end index of the coloured-to-be characters in the string e.g.
var main_string = "Hello World"
var string_to_color = "World"
var range = (main_string as NSString).rangeOfString(string_to_color)
Then you convert to attributed string and use 'add attribute' with NSForegroundColorAttributeName:
var attributedString = NSMutableAttributedString(string:main_string)
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor() , range: range)
A list of further standard attributes you can set can be found in Apple's documentation
Swift 2.1 Update:
let text = "We tried to make this app as most intuitive as possible for you. If you have any questions don't hesitate to ask us. For a detailed manual just click here."
let linkTextWithColor = "click here"
let range = (text as NSString).rangeOfString(linkTextWithColor)
let attributedString = NSMutableAttributedString(string:text)
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor() , range: range)
self.helpText.attributedText = attributedString
self.helpText is a UILabel outlet.
Swift 4.2 and Swift 5 colorise parts of the string.
A very easy way to use NSMutableAttributedString while extending the String. This also can be used to colourize more than one word in the whole string.
import UIKit
extension String {
func attributedStringWithColor(_ strings: [String], color: UIColor, characterSpacing: UInt? = nil) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: self)
for string in strings {
let range = (self as NSString).range(of: string)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
}
guard let characterSpacing = characterSpacing else {return attributedString}
attributedString.addAttribute(NSAttributedString.Key.kern, value: characterSpacing, range: NSRange(location: 0, length: attributedString.length))
return attributedString
}
}
Now you can use globally at any viewcontroller you want:
let attributedWithTextColor: NSAttributedString = "Doc, welcome back :)".attributedStringWithColor(["Doc", "back"], color: UIColor.black)
myLabel.attributedText = attributedWithTextColor
Answer is already given in previous posts but i have a different way of doing this
Swift 3x :
var myMutableString = NSMutableAttributedString()
myMutableString = NSMutableAttributedString(string: "Your full label textString")
myMutableString.setAttributes([NSFontAttributeName : UIFont(name: "HelveticaNeue-Light", size: CGFloat(17.0))!
, NSForegroundColorAttributeName : UIColor(red: 232 / 255.0, green: 117 / 255.0, blue: 40 / 255.0, alpha: 1.0)], range: NSRange(location:12,length:8)) // What ever range you want to give
yourLabel.attributedText = myMutableString
Hope this helps anybody!
Chris' answer was a great help to me, so I used his approach and turned into a func that I can reuse. This let's me assign a color to a substring while giving the rest of the string another color.
static func createAttributedString(fullString: String, fullStringColor: UIColor, subString: String, subStringColor: UIColor) -> NSMutableAttributedString
{
let range = (fullString as NSString).rangeOfString(subString)
let attributedString = NSMutableAttributedString(string:fullString)
attributedString.addAttribute(NSForegroundColorAttributeName, value: fullStringColor, range: NSRange(location: 0, length: fullString.characters.count))
attributedString.addAttribute(NSForegroundColorAttributeName, value: subStringColor, range: range)
return attributedString
}
Swift 4.1
NSAttributedStringKey.foregroundColor
for example if you want to change font in NavBar:
self.navigationController?.navigationBar.titleTextAttributes = [ NSAttributedStringKey.font: UIFont.systemFont(ofSize: 22), NSAttributedStringKey.foregroundColor: UIColor.white]
You can use this extension
I test it over
swift 4.2
import Foundation
import UIKit
extension NSMutableAttributedString {
convenience init (fullString: String, fullStringColor: UIColor, subString: String, subStringColor: UIColor) {
let rangeOfSubString = (fullString as NSString).range(of: subString)
let rangeOfFullString = NSRange(location: 0, length: fullString.count)//fullString.range(of: fullString)
let attributedString = NSMutableAttributedString(string:fullString)
attributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: fullStringColor, range: rangeOfFullString)
attributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: subStringColor, range: rangeOfSubString)
self.init(attributedString: attributedString)
}
}
Swift 2.2
var myMutableString = NSMutableAttributedString()
myMutableString = NSMutableAttributedString(string: "1234567890", attributes: [NSFontAttributeName:UIFont(name: kDefaultFontName, size: 14.0)!])
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor(red: 0.0/255.0, green: 125.0/255.0, blue: 179.0/255.0, alpha: 1.0), range: NSRange(location:0,length:5))
self.lblPhone.attributedText = myMutableString
Easiest way to do label with different style such as color, font etc. is use property "Attributed" in Attributes Inspector. Just choose part of text and change it like you want
Based on the answers before I created a string extension
extension String {
func highlightWordsIn(highlightedWords: String, attributes: [[NSAttributedStringKey: Any]]) -> NSMutableAttributedString {
let range = (self as NSString).range(of: highlightedWords)
let result = NSMutableAttributedString(string: self)
for attribute in attributes {
result.addAttributes(attribute, range: range)
}
return result
}
}
You can pass the attributes for the text to the method
Call like this
let attributes = [[NSAttributedStringKey.foregroundColor:UIColor.red], [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 17)]]
myLabel.attributedText = "This is a text".highlightWordsIn(highlightedWords: "is a text", attributes: attributes)
Swift 4.1
I have changed from this
In Swift 3
let str = "Welcome "
let welcomeAttribute = [ NSForegroundColorAttributeName: UIColor.blue()]
let welcomeAttrString = NSMutableAttributedString(string: str, attributes: welcomeAttribute)
And this in Swift 4.0
let str = "Welcome "
let welcomeAttribute = [ NSAttributedStringKey.foregroundColor: UIColor.blue()]
let welcomeAttrString = NSMutableAttributedString(string: str, attributes: welcomeAttribute)
to Swift 4.1
let str = "Welcome "
let welcomeAttribute = [ NSAttributedStringKey(rawValue: NSForegroundColorAttributeName): UIColor.blue()]
let welcomeAttrString = NSMutableAttributedString(string: str, attributes: welcomeAttribute)
Works fine
swift 4.2
let textString = "Hello world"
let range = (textString as NSString).range(of: "world")
let attributedString = NSMutableAttributedString(string: textString)
attributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.red, range: range)
self.textUIlable.attributedText = attributedString
This might be work for you
let main_string = " User not found,Want to review ? Click here"
let string_to_color = "Click here"
let range = (main_string as NSString).range(of: string_to_color)
let attribute = NSMutableAttributedString.init(string: main_string)
attribute.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue , range: range)
lblClickHere.attributedText = attribute
With this simple function you can assign the text and highlight the chosen word.
You can also change the UITextView to UILabel, etc.
func highlightBoldWordAtLabel(textViewTotransform: UITextView, completeText: String, wordToBold: String){
textViewToTransform.text = completeText
let range = (completeText as NSString).range(of: wordToBold)
let attribute = NSMutableAttributedString.init(string: completeText)
attribute.addAttribute(NSAttributedString.Key.font, value: UIFont.boldSystemFont(ofSize: 16), range: range)
attribute.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.black , range: range)
textViewToTransform.attributedText = attribute
}
For everyone who are looking for "Applying specific color to multiple words in text", we can do it using NSRegularExpression
func highlight(matchingText: String, in text: String) {
let attributedString = NSMutableAttributedString(string: text)
if let regularExpression = try? NSRegularExpression(pattern: "\(matchingText)", options: .caseInsensitive) {
let matchedResults = regularExpression.matches(in: text, options: [], range: NSRange(location: 0, length: attributedString.length))
for matched in matchedResults {
attributedString.addAttributes([NSAttributedStringKey.backgroundColor : UIColor.yellow], range: matched.range)
}
yourLabel.attributedText = attributedString
}
}
Reference link : https://gist.github.com/aquajach/4d9398b95a748fd37e88
You can use as simple extension
extension String{
func attributedString(subStr: String) -> NSMutableAttributedString{
let range = (self as NSString).range(of: subStr)
let attributedString = NSMutableAttributedString(string:self)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red , range: range)
return attributedString
}
}
myLable.attributedText = fullStr.attributedString(subStr: strToChange)
This extension works well when configuring the text of a label with an already set default color.
public extension String {
func setColor(_ color: UIColor, ofSubstring substring: String) -> NSMutableAttributedString {
let range = (self as NSString).range(of: substring)
let attributedString = NSMutableAttributedString(string: self)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
return attributedString
}
}
For example
let text = "Hello World!"
let attributedText = text.setColor(.blue, ofSubstring: "World")
let myLabel = UILabel()
myLabel.textColor = .white
myLabel.attributedText = attributedText
Super easy way to do this.
let text = "This is a colorful attributed string"
let attributedText =
NSMutableAttributedString.getAttributedString(fromString: text)
attributedText.apply(color: .red, subString: "This")
//Apply yellow color on range
attributedText.apply(color: .yellow, onRange: NSMakeRange(5, 4))
For more detail click here:
https://github.com/iOSTechHub/AttributedString
To change color of the font colour, first select attributed instead of plain like in the image below
You then need to select the text in the attributed field and then select the color button on the right-hand side of the alignments. This will change the color.
You can use this method. I implemented this method in my common utility class to access globally.
func attributedString(with highlightString: String, normalString: String, highlightColor: UIColor) -> NSMutableAttributedString {
let attributes = [NSAttributedString.Key.foregroundColor: highlightColor]
let attributedString = NSMutableAttributedString(string: highlightString, attributes: attributes)
attributedString.append(NSAttributedString(string: normalString))
return attributedString
}
If you are using Swift 3x and UITextView, maybe the NSForegroundColorAttributeName won't work (it didn't work for me no matter what approach I tried).
So, after some digging around I found a solution.
//Get the textView somehow
let textView = UITextView()
//Set the attributed string with links to it
textView.attributedString = attributedString
//Set the tint color. It will apply to the link only
textView.tintColor = UIColor.red
You need to change textview parameters, not parameters of attributed string
textView.linkTextAttributes = [
NSAttributedString.Key.foregroundColor: UIColor.red,
NSAttributedString.Key.underlineColor: UIColor.red,
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue
]
Please check cocoapod Prestyler:
Prestyler.defineRule("$", UIColor.orange)
label.attributedText = "This $text$ is orange".prestyled()
extension String{
// to make text field mandatory * looks
mutating func markAsMandatoryField()-> NSAttributedString{
let main_string = self
let string_to_color = "*"
let range = (main_string as NSString).range(of: string_to_color)
print("The rang = \(range)")
let attribute = NSMutableAttributedString.init(string: main_string)
attribute.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.rgbColor(red: 255.0, green: 0.0, blue: 23.0) , range: range)
return attribute
}
}
use
EmailLbl.attributedText = EmailLbl.text!.markAsMandatoryField()

Resources