Swift - HTML stylised NSAttributedString with links - swift

I have a UITextView and I have a string of HTML I give it like so...
func applyHTML() {
textView.attributedText = htmlString()
textView.linkTextAttributes = [NSForegroundColorAttributeName: .blue,
NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue,
NSFontAttributeName:font]
}
func htmlString() -> NSAttributedString? {
if let htmlData = text.dataUsingEncoding(NSUnicodeStringEncoding) {
do {
let attributedString = try NSMutableAttributedString(data: htmlData,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
return attributedString
}
catch { return nil }
}
return nil
}
This works fine, I get blue hyperlinks with black text... what I want to do now is style the font for all and colour of the non-hyperlink text... problem is when I do that, by adding this....
let length = attributedString.length
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .Center
attributedString.setAttributes([NSForegroundColorAttributeName:.white,NSFontAttributeName: myFont,NSParagraphStyleAttributeName: paragraphStyle], range: NSRange(location: 0, length: length))
it removes the hyperlinks... I want them to work in tandem. What am I doing wrong?

For anyone who visits, #Larme was right
attributedString.setAttributes([NSForegroundColorAttributeName:.white,NSFontAttributeName: myFont,NSParagraphStyleAttributeName: paragraphStyle], range: NSRange(location: 0, length: length))
should be
attributedString.addAttributes([NSForegroundColorAttributeName:.white,NSFontAttributeName: myFont,NSParagraphStyleAttributeName: paragraphStyle], range: NSRange(location: 0, length: length))

Related

Highlight and Unhighlight Searched Text in UITableView

I want to highlight and unhighlight the searched text in UITableView.
For Highlighting the searched text, i have tried below code in CellForRow and Highlighting searched text works fine.
let range = (self.modelClass.users[indexPath.row].name as NSString).range(of: self.searchBar.text!, options: .caseInsensitive)
let attributedString = NSMutableAttributedString(string: self.modelClass.users[indexPath.row].name)
attributedString.addAttributes([NSAttributedString.Key.backgroundColor: Colors.highlightedColor, NSAttributedString.Key.font: cell.lblName.font], range: range)
cell.lblName.attributedText = attributedString
EDIT 1
if searchBar.text != "" {
let range = (self.modelClass.users[indexPath.row].name as NSString).range(of: self.searchBar.text!, options: .caseInsensitive)
let attributedString = NSMutableAttributedString(string: self.modelClass.users[indexPath.row].name)
attributedString.addAttributes([NSAttributedString.Key.backgroundColor: Colors.highlightedColor, NSAttributedString.Key.font: cell.lblName.font], range: range)
cell.lblName.attributedText = attributedString
}
else {
cell.lblName.text = self.modelClass.users[indexPath.row].name
}
EDIT 2
Actually me 1st code is working fine. First i assign normal text to lblName.text and then after again i set attributedText to lblName.attributedText. When i comment the normal text assign, it works fine.
But when i clear searched data and load my default array but the highlighted color wasn't clear in lblName UILabel.
Please guide me how can i unhighlight the UILabel text?
in cellForRowAt, You also need to pass an else condition for the default label view.
if self.searchBar.text != "" {
let range = (self.modelClass.users[indexPath.row].name as NSString).range(of:
self.searchBar.text!, options: .caseInsensitive)
let attributedString = NSMutableAttributedString(string:
self.modelClass.users[indexPath.row].name)
attributedString.addAttributes([NSAttributedString.Key.backgroundColor:
Colors.highlightedColor, NSAttributedString.Key.font: cell.lblName.font], range: range)
cell.lblName.attributedText = attributedString
}
else {
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: self.modelClass.users[indexPath.row].name as NSString)
attributeString.removeAttribute(NSAttributedString.Key.backgroundColor, range: NSMakeRange(0, attributeString.length))
cell.lblName.attributedText = attributeString
}
Reload TableView when searchBar text changes.
I wrote an extension for UILabel to manage this
extension UILabel {
func stringWithSearchBarString(_ string: String) {
let attributedString = NSMutableAttributedString(string: text ?? "")
if string.count == 0 {
attributedText = attributedString
return
}
let dotRanges: [NSRange]
do {
let regex = try NSRegularExpression(pattern: string.lowercased(), options: [])
dotRanges = regex.matches(in: string.lowercased(), options: [], range: NSMakeRange(0, string.count)).map {$0.range}
} catch {
dotRanges = []
}
let rangeColor = WXColors.mainAppColor.color
for dotRange in dotRanges {
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: rangeColor, range: dotRange)
}
attributedText = attributedString
}
}
USAGE:
cell.textLabel.stringWithSearchBarString(searcher.text)

Change the style of an array of String inside a TextView - swift - EDIT

My first question was how to change the Font of a word example "test" in a textView, and it was answered correctly by #bkrl and #Torongo.
func changeAllOccurence(of string: String, font: UIFont) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: self)
var range = NSMakeRange(0, attributedString.mutableString.length)
while(range.location != NSNotFound)
{
range = attributedString.mutableString.range(of: string, options: .caseInsensitive, range: range)
if(range.location != NSNotFound)
{
attributedString.addAttribute(NSFontAttributeName,
value: font,
range: range)
range = NSMakeRange(range.location + range.length, self.characters.count - (range.location + range.length));
}
}
return attributedString
}
As I am still not familiar with the above code, I tried to add few lines in order to generalize the code so it can works for an array of strings and not only one string.
But for sure it didn't works cause it changed the font of the last word only which is reasonable cause the final changes will be for the last word which is "usage":
let words = ["example", "usage"]
for word in words {
let attributedText = text.changeAllOccurence(of: word, font: UIFont.boldSystemFont(ofSize: 17))
textview.attributedText = attributedText
}
Can someone advise how to improve the code provided by #Toromgo in order to work for any array of strings instead of just one?
You could create extension like:
extension String {
func change(font: UIFont, of string: String) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: self)
let subStringRange = attributedString.mutableString.range(of: string,
options: .caseInsensitive)
if subStringRange.location != NSNotFound {
attributedString.addAttribute(NSFontAttributeName,
value: font,
range: subStringRange)
}
return attributedString
}
}
Usage:
let text = "This is example of usage extension"
let attributedText = text.change(font: UIFont.boldSystemFont(ofSize: 17), of: "example")
textView.attributedText = attributedText
Hope that helps!
As you have changed your question, ihave updated my answer accordingly. Please try this:
extension String {
func changeAllOccurence(of strings: [String], font: UIFont) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: self)
for eachString in strings {
var range = NSMakeRange(0, attributedString.mutableString.length)
while(range.location != NSNotFound)
{
range = attributedString.mutableString.range(of: eachString, options: .caseInsensitive, range: range)
if(range.location != NSNotFound)
{
attributedString.addAttribute(NSFontAttributeName,
value: font,
range: range)
range = NSMakeRange(range.location + range.length, self.characters.count - (range.location + range.length));
}
}
}
return attributedString
}
}
I have run the code and it is working.

UITextView with hyperlink text

With a non-editable UITextView, I would like to embed text like this in iOS9+:
Just click here to register
I can create a function and manipulate the text but is there a simpler way?
I see that I can use NSTextCheckingTypeLink so getting the text clickable without the 'click here' part is straightforward in Interface Builder:
Just http://example.com to register
I'm using Xcode 8 and Swift 3 if that's relevant.
Set isEditable = false or the text view will go into text-editing mode when user taps on it.
Swift 4 and later
let attributedString = NSMutableAttributedString(string: "Just click here to register")
let url = URL(string: "https://www.apple.com")!
// Set the 'click here' substring to be the link
attributedString.setAttributes([.link: url], range: NSMakeRange(5, 10))
self.textView.attributedText = attributedString
self.textView.isUserInteractionEnabled = true
self.textView.isEditable = false
// Set how links should appear: blue and underlined
self.textView.linkTextAttributes = [
.foregroundColor: UIColor.blue,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
If you want to use multiple hyperlinks you can use this alternative for Swift 5
extension UITextView {
func addHyperLinksToText(originalText: String, hyperLinks: [String: String]) {
let style = NSMutableParagraphStyle()
style.alignment = .left
let attributedOriginalText = NSMutableAttributedString(string: originalText)
for (hyperLink, urlString) in hyperLinks {
let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
let fullRange = NSRange(location: 0, length: attributedOriginalText.length)
attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: urlString, range: linkRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: YourFont, range: fullRange)
}
self.linkTextAttributes = [
NSAttributedString.Key.foregroundColor: YourColor,
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue,
]
self.attributedText = attributedOriginalText
}
}
Usage:
yourTextView.addHyperLinksToText(originalText: "Testing hyperlinks here and there", hyperLinks: ["here": "someUrl1", "there": "someUrl2"])
The same solution for Swift 3 using extensions :
A. Add extension -
extension UITextView {
func hyperLink(originalText: String, hyperLink: String, urlString: String) {
let style = NSMutableParagraphStyle()
style.alignment = .center
let attributedOriginalText = NSMutableAttributedString(string: originalText)
let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
let fullRange = NSMakeRange(0, attributedOriginalText.length)
attributedOriginalText.addAttribute(NSLinkAttributeName, value: urlString, range: linkRange)
attributedOriginalText.addAttribute(NSParagraphStyleAttributeName, value: style, range: fullRange)
attributedOriginalText.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 10), range: fullRange)
self.linkTextAttributes = [
NSForegroundColorAttributeName: UIConfig.primaryColour,
NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
]
self.attributedText = attributedOriginalText
}
}
B. Add link url - let linkUrl = "https://www.my_website.com"
C. Implement UITextViewDelegate in your ViewController like this -
class MyViewController: UIViewController, UITextViewDelegate {
}
D. Add delegate method to handle tap events -
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
if (URL.absoluteString == linkUrl) {
UIApplication.shared.openURL(URL)
}
return false
}
}
E. And finally, things to make sure for your UITextView under attribute inspector -
Behaviour - Editable is turned OFF & Selectable is turned ON.
Data Detectors - Link is turned ON.
Usage -
textView.hyperLink(originalText: "To find out more please visit our website", hyperLink: "website", urlString: linkUrl)
Cheers & happy coding!
Swift 5
This is based on Tejas' answer as a few items in both classes were deprecated.
extension UITextView {
func hyperLink(originalText: String, hyperLink: String, urlString: String) {
let style = NSMutableParagraphStyle()
style.alignment = .left
let attributedOriginalText = NSMutableAttributedString(string: originalText)
let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
let fullRange = NSMakeRange(0, attributedOriginalText.length)
attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: urlString, range: linkRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.blue, range: fullRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
self.linkTextAttributes = [
kCTForegroundColorAttributeName: UIColor.blue,
kCTUnderlineStyleAttributeName: NSUnderlineStyle.single.rawValue,
] as [NSAttributedString.Key : Any]
self.attributedText = attributedOriginalText
}
Don't forget to add UITextViewDelegate to your view controller and set your let linkUrl = "https://example.com"
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
if (URL.absoluteString == linkUrl) {
UIApplication.shared.open(URL) { (Bool) in
}
}
return false
}
Usage stays the same:
textView.hyperLink(originalText: "To find out more please visit our website", hyperLink: "website", urlString: linkUrl)
Swift 4 code.
May be I'm the only one who needs to set several links and color the words in one message. I created an AttribTextHolder class to accumulate all information about text inside this holder and easily pass it between objects to set text to UITextView somewhere deep inside a controller.
class AttribTextHolder {
enum AttrType {
case link
case color
}
let originalText: String
var attributes: [(text: String, type: AttrType, value: Any)]
init(text: String, attrs: [(text: String, type: AttrType, value: Any)] = [])
{
originalText = text
attributes = attrs
}
func addAttr(_ attr: (text: String, type: AttrType, value: Any)) -> AttribTextHolder {
attributes.append(attr)
return self
}
func setTo(textView: UITextView)
{
let style = NSMutableParagraphStyle()
style.alignment = .left
let attributedOriginalText = NSMutableAttributedString(string: originalText)
for item in attributes {
let arange = attributedOriginalText.mutableString.range(of: item.text)
switch item.type {
case .link:
attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: item.value, range: arange)
case .color:
var color = UIColor.black
if let c = item.value as? UIColor { color = c }
else if let s = item.value as? String { color = s.color() }
attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: arange)
default:
break
}
}
let fullRange = NSMakeRange(0, attributedOriginalText.length)
attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
textView.linkTextAttributes = [
kCTForegroundColorAttributeName: UIColor.blue,
kCTUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
] as [String : Any]
textView.attributedText = attributedOriginalText
}
}
Use it like this:
let txt = AttribTextHolder(text: "To find out more visit our website or email us your questions")
.addAttr((text: "our website", type: .link, "http://example.com"))
.addAttr((text: "our website", type: .color, "#33BB22"))
.addAttr((text: "email us", type: .link, "mailto:us#example.com"))
.addAttr((text: "email us", type: .color, UIColor.red))
....
....
txt.setTo(textView: myUITextView)
Also in this code I use simple String extension to convert String hex values into UIColor objects
extension String {
/// Converts string color (ex: #23FF33) into UIColor
func color() -> UIColor {
let hex = self.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int = UInt32()
Scanner(string: hex).scanHexInt32(&int)
let a, r, g, b: UInt32
switch hex.characters.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (255, 0, 0, 0)
}
return UIColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
}
}
Using Swift >= 4:
let descriptionText = NSMutableAttributedString(string:"To learn more, check out our ", attributes: [:])
let linkText = NSMutableAttributedString(string: "Privacy Policy and Terms of Use", attributes: [NSAttributedString.Key.link: URL(string: example.com)!])
descriptionText.append(linkText)
The same solution for Swift 4 using extensions:
extension UITextView {
func hyperLink(originalText: String, hyperLink: String, urlString: String) {
let style = NSMutableParagraphStyle()
style.alignment = .left
let attributedOriginalText = NSMutableAttributedString(string: originalText)
let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
let fullRange = NSMakeRange(0, attributedOriginalText.length)
attributedOriginalText.addAttribute(NSAttributedStringKey.link, value: urlString, range: linkRange)
attributedOriginalText.addAttribute(NSAttributedStringKey.paragraphStyle, value: style, range: fullRange)
attributedOriginalText.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue, range: fullRange)
attributedOriginalText.addAttribute(NSAttributedStringKey.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
self.linkTextAttributes = [
kCTForegroundColorAttributeName: UIColor.blue,
kCTUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
] as [String : Any]
self.attributedText = attributedOriginalText
}
}
A safer solution to implement hyperlink via UITextView
var termsConditionsTextView: UITextView = {
let view = UITextView()
view.backgroundColor = .clear
view.textAlignment = .left
let firstTitleString = "By registering for THIS_APP I agree with the "
let secondTitleString = "Terms & Conditions"
let finishTitleString = firstTitleString + secondTitleString
let attributedString = NSMutableAttributedString(string: finishTitleString)
attributedString.addAttribute(.link, value: "https://stackoverflow.com", range: NSRange(location: firstTitleString.count, length: secondTitleString.count))
view.attributedText = attributedString
view.textContainerInset = .zero
view.linkTextAttributes = [
.foregroundColor: UIColor.blue,
.underlineStyle: NSUnderlineStyle.single.isEmpty
]
view.font = view.font = UIFont(name: "YOUR_FONT_NAME", size: 16)
view.textColor = UIColor.black
return view }()
SWIFT 5 AND MORE THAN ONE LINK
import UIKit
public extension UITextView {
func hyperLink(originalText: String, linkTextsAndTypes: [String: String]) {
let style = NSMutableParagraphStyle()
style.alignment = .left
let attributedOriginalText = NSMutableAttributedString(string: originalText)
for linkTextAndType in linkTextsAndTypes {
let linkRange = attributedOriginalText.mutableString.range(of: linkTextAndType.key)
let fullRange = NSRange(location: 0, length: attributedOriginalText.length)
attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: linkTextAndType.value, range: linkRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.blue, range: fullRange)
attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
}
self.linkTextAttributes = [
kCTForegroundColorAttributeName: UIColor.blue,
kCTUnderlineStyleAttributeName: NSUnderlineStyle.single.rawValue
] as [NSAttributedString.Key: Any]
self.attributedText = attributedOriginalText
}
}
And the usage in your viewController:
#IBOutlet weak var termsHyperlinkTextView: UITextView! {
didSet {
termsHyperlinkTextView.delegate = self
termsHyperlinkTextView.hyperLink(originalText: "Check out terms & conditions or our privacy policy",
linkTextsAndTypes: ["terms & conditions": LinkType.termsAndConditions.rawValue,
"privacy policy": LinkType.privacyPolicy.rawValue])
}
}
enum LinkType: String {
case termsAndConditions
case privacyPolicy
}
// MARK: - UITextViewDelegate
extension ViewController: UITextViewDelegate {
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
if let linkType = LinkType(rawValue: URL.absoluteString) {
// TODO: handle linktype here with switch or similar.
}
return false
}
}
You could use this simple method to add a hyperlink to any set of characters starting with tag
func addLink(forString string : NSMutableAttributedString
,baseURL : String
,tag : String){
let array = string.string.replacingOccurrences(of: "\n", with: " ").components(separatedBy: " ")
let filterArray = array.filter { (string) -> Bool in
return string.contains(tag)
}
for element in filterArray {
let removedHashtag = element.replacingOccurrences(of: tag, with: "")
let url = baseURL + removedHashtag
let range = NSString.init(string: (string.string)).range(of: element)
string.addAttributes([NSAttributedStringKey.link : url.replacingOccurrences(of: " ", with: "")], range: range)
}
}
I wanted to do the same thing and ended up just using a UIButton with the title "click here" surrounded by UILabels "just " and " to register", and then:
#IBAction func btnJustClickHereLink(_ sender: UIButton) {
if let url = URL(string: "http://example.com") {
UIApplication.shared.openURL(url)
}
}

Swift Text Attributes Color [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()

Swift: fill AutoreleasingUnsafeMutablePointer<NSDictionary?>

I am DTCoreText to transform HTML to attributed text. Because I want to set the font up front, not afterwards as that would override all bold, italic etcetera tags I want to set the document attributes in the constructor. This constructors wants me to give a AutoreleasingUnsafeMutablePointer that more or less seems to be a NSDictionary? with & up front. Sort of. Only it doesn't let me set it in any way. I've tried .memory, tried to cast the dictionary in any possible way and it just doesn't accept any data.
let font = UIFont.systemFontOfSize(12)
let data = info.desc?.dataUsingEncoding(NSUTF8StringEncoding)
let attributes: NSMutableDictionary? = NSMutableDictionary()
attributes!.setObject(font, forKey: NSFontAttributeName)
var attributeRef: AutoreleasingUnsafeMutablePointer<NSDictionary?> = AutoreleasingUnsafeMutablePointer.null()
NSMutableAttributedString(HTMLData: data, documentAttributes: nil)
//attributeRef = *attributeDict
let attributedString = NSMutableAttributedString(HTMLData: data, documentAttributes:attributeRef)
let paragraphStyle: NSMutableParagraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = NSLineBreakMode.ByWordWrapping;
let range = NSMakeRange(0, attributedString.length)
attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: range)
lblMessage.attributedText = attributedString
You should not be using DTCoreText at this point; iOS now has native calls for this. Just say var dict = NSDictionary?() and pass &dict. Here's example code:
let s = "<html><body><h1>Howdy</h1><p>Hello</p></body></html>"
let d = s.dataUsingEncoding(NSUTF16StringEncoding, allowLossyConversion: false)
var dict = NSDictionary?()
let att = NSAttributedString(data: d!, options: [
NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType
], documentAttributes: &dict, error: nil)
println(att!)
println(dict!)
You'll see that this works perfectly well. Here is dict:
BottomMargin = 72;
Converted = "-1";
DocumentType = NSHTML;
LeftMargin = 90;
PaperMargin = "UIEdgeInsets: {72, 90, 72, 90}";
PaperSize = "NSSize: {612, 792}";
RightMargin = 90;
TopMargin = 72;
UTI = "public.html";
However, I usually pass nil because nothing is coming back in the second dictionary that I really care about.
To update fonts in generated HTML use the code similar to the following:
Swift 5
extension String {
/// Convert HTML to NSAttributedString
func convertHtml() -> NSAttributedString {
guard let data = data(using: .utf8) else { return NSAttributedString() }
if let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
let string = NSMutableAttributedString(attributedString: attributedString)
// Apply text color
string.addAttributes([.foregroundColor: UIColor.text], range: NSRange(location: 0, length: attributedString.length))
// Update fonts
let regularFont = UIFont(name: Fonts.Regular, size: 13)! // DEFAULT FONT (REGUALR)
let boldFont = UIFont(name: Fonts.Bold, size: 13)! // BOLD FONT
/// add other fonts if you have them
string.enumerateAttribute(.font, in: NSMakeRange(0, attributedString.length), options: NSAttributedString.EnumerationOptions(rawValue: 0), using: { (value, range, stop) -> Void in
/// Update to our font
// Bold font
if let oldFont = value as? UIFont, oldFont.fontName.lowercased().contains("bold") {
string.removeAttribute(.font, range: range)
string.addAttribute(.font, value: boldFont, range: range)
}
// Default font
else {
string.addAttribute(.font, value: regularFont, range: range)
}
})
return string
}
return NSAttributedString()
}
}