I working with highlighting of particular character in a given text.
Here is my worked code,
let titleLabel: UILabel!
let myText = "கொக்கு"
let textToHighlight = "க்"
titleLabel.frame = CGRect(origin: .zero, size: CGSize(width: 100, height: 100))
let textStroke: [NSAttributedString.Key : Any] = [
.strokeColor : UIColor.white,
.foregroundColor : UIColor.red,
.strokeWidth : -2.0,
]
let textTitle = NSMutableAttributedString(string: myText)
let textHighlightRange = (myText as NSString).range(of: textToHighlight)
textTitle.addAttributes(textStroke, range: textHighlightRange)
titleLabel.attributedText = textTitle
The other Texts which i worked and getting the expected results, but only few words specific in tamil languages am facing issues,
Other Languages Text working fine were,
let myText = "MyText"
let textToHighlight = "T"
let myText = "मानक हिन्दी"
let textToHighlight = "न"
Facing Issues with the text,
let myText = "கொக்கு"
let textToHighlight = "க்"
As i googled and got few details from the link
NSRange in Strings having dialects.
But how to find() and advance() methods used in the given reference.
I am using xcode 11.2 and swift version 4
EDIT:
After a long time, i found a nearest solution to highlight / change foreground color of my matched character in the text as below,
func characterToHighlight(text: String, highlight: String) -> NSAttributedString? {
guard let match = try? NSRegularExpression(pattern: highlight, options: .caseInsensitive) else {
return nil
}
let textStroke: [NSAttributedString.Key : Any] = [
.strokeColor : UIColor.white,
.foregroundColor : UIColor.red,
.strokeWidth : -2.0,
]
let attributedString = NSMutableAttributedString(string: text)
match
.matches(in: text, options: .withTransparentBounds,
range: NSRange(location: 0, length: text.utf16.count))
.forEach {
attributedString.addAttributes(textStroke, range: $0.range)
}
return attributedString
}
if let matchedCharacter = characterToHighlight(text: text, highlight: textToHighlight){titleLabel.attributedText = matchedCharacter}
This code works for the matched character of tamil letters in the string but the foreground of the other letters were black.
I need the non-matched characters in blue color and the matched characters in red color.
How to achieve this?
ANSWER:
let titleLabel: UILabel!
let myText = "கொக்கு"
let textToHighlight = "க்"
titleLabel.frame = CGRect(origin: .zero, size: CGSize(width: 100, height: 100))
let textStroke: [NSAttributedString.Key : Any] = [
.strokeColor : UIColor.white,
.foregroundColor : UIColor.blue,
.strokeWidth : -2.0,
]
let highlightTextAttributes: [NSAttributedString.Key : Any] = [
.strokeColor : UIColor.white,
.foregroundColor : UIColor.red,
.strokeWidth : -4.0,
]
let textTitle = NSMutableAttributedString(string: myText)
textTitle.addAttributes(textStroke, range: textHighlightRange)
if let matchedCharacterRange = getRange(text: text, highlight: textToHighlight)
{
textTitle.addAttributes(highlightTextAttributes, range: matchedCharacterRange)
}
titleLabel.attributedText = textTitle
func getRange(text: String, highlight: String) -> NSRange? {
guard let regex = try? NSRegularExpression(pattern: highlight, options: .caseInsensitive) else {
return nil
}
var highlightRange: NSRange?
regex
.matches(in: text, options: .withTransparentBounds,
range: NSRange(location: 0, length: text.utf16.count))
.forEach {
highlightRange = $0.range
}
return highlightRange
}
I am unable to post my answer separately, so i am editing my question and posting the answer in the same.
Experts, please review and correct me if am wrong.
Thanks for your support.
When a string starts and ends with an underscore, I am making that string italic. After that, I am removing the underscores. This works fine if the string is like this "_hello_ world"
However, this doesn't work => "_hello_ world _happy_"
This is my regex => "\\_(.*?)\\_"
func applyItalicFormat(string: String) {
let matches = RegexPattern.italicRegex.matches(string)
for match in matches {
let mRange = match.range
self.addAttributes([NSAttributedStringKey.font : UIFont.latoMediumItalic(size: 15)],
range: mRange)
if let rangeObj = Range(NSMakeRange(mRange.location, mRange.upperBound), in: string) {
var sub = string.substring(with: rangeObj)
sub = sub.replacingOccurrences(of: "_", with: "")
print("sub is \(sub)")
replaceCharacters(in: mRange, with: sub)
} else {
}
}
}
Another Regex format, \\_(?:(?!_).)+\\_ and using map
var mySentence = "This is from _mcdonal_ mac _system_ which says that _below_ answer is one of the _easiest_ way"
var wholeText = NSMutableAttributedString()
override func viewDidLoad() {
super.viewDidLoad()
wholeText = NSMutableAttributedString(string: mySentence)
italicLbl.attributedText = matches(for: "\\_(?:(?!_).)+\\_", in: mySentence)
}
func matches(for regex: String, in text: String) -> NSAttributedString {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text, range: NSRange(text.startIndex..., in: text))
let _ = results.map { (val) in
wholeText.addAttributes([NSAttributedString.Key.font : UIFont.italicSystemFont(ofSize: 17)],
range: val.range)
var sub = String(text[Range(val.range, in: text)!])
sub = sub.replacingOccurrences(of: "_", with: " ")
wholeText.replaceCharacters(in: val.range, with: sub)
}
return wholeText
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return wholeText
}
}
Screenshot
With small modifications, and use of range(at:) of the matches:
extension NSMutableAttributedString {
func applyItalicFormat(pattern: String) {
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matches(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count))
let italicAttributes = [NSAttributedString.Key.font: UIFont.italicSystemFont(ofSize: 15)]
for match in matches.reversed() {
let textRange = match.range(at: 1)
let attributedTextToChange = NSMutableAttributedString(attributedString: self.attributedSubstring(from: textRange))
attributedTextToChange.addAttributes(italicAttributes, range: NSRange(location: 0, length: attributedTextToChange.length))
replaceCharacters(in: match.range, with: attributedTextToChange)
}
}
}
You don't need to replace the _, you have already the good range of the text alone without the underscores.
I use the matches.reversed(), because when you apply the first one, then the range of the second already found is not correct anymore (you remove twice _).
I prefer to extract the attributedString part to modify, modify it, and then replace it with the modified it.
I simplified some rest of the code.
Sample Test (usable in Playground):
let initialTexts = ["_hello_ world", "\n\n", "_hello_ world _happy_"]
let label = UILabel.init(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
label.backgroundColor = .orange
label.numberOfLines = 0
let attr = NSMutableAttributedString()
for anInitialText in initialTexts {
let attributedStr = NSMutableAttributedString(string: anInitialText)
attributedStr.applyItalicFormat(pattern: "\\_(.*?)\\_")
attr.append(attributedStr)
}
label.attributedText = attr
Before, I changed specific strings to NSTextAttachment that include image to display custom emoticon.
String to NSTextAttachment code
{
guard
let original = self.attributedText
else { return }
let pattern = "\\[img src=(\\w+)\\]"
do{
let regex = try NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matches(in: original.string, options : [], range : NSMakeRange(0, original.string.characters.count))
let attributeString = NSMutableAttributedString(attributedString: original)
for match in matches.reversed(){
let emoticonString = attributeString.attributedSubstring(from: match.rangeAt(1)).string
if let emoticonAndroid = Emoticon(rawValue: emoticonString),
let image = UIImage(named : "\(emoticonAndroid.convertFromAndroid().rawValue)_000"){
image.accessibilityIdentifier = emoticonAndroid.rawValue
let attributedImage = NSTextAttachment()
attributedImage.image = image
attributedImage.bounds = CGRect(x: 0, y: -8, width: 25, height: 25)
attributeString.beginEditing()
attributeString.replaceCharacters(in: match.rangeAt(0), with: NSAttributedString(attachment: attributedImage))
attributeString.endEditing()
}
}
self.attributedText = attributeString
}catch{
return
}
}
but, I need to replace NSTextAttachment to string to send message.
I used NSMutableAttributedString.replaceCharacters(in:with:) method. but, It can work with only one emoticon image.
one emoticon
two emoticons or more
how can I fix it?
NSTextAttachment to String code
{
if let original = self.attributedText{
let attributeString = NSMutableAttributedString(attributedString: original)
original.enumerateAttribute(NSAttachmentAttributeName, in: NSMakeRange(0, original.length), options: [], using: { attribute, range, _ in
if let attachment = attribute as? NSTextAttachment,
let image = attachment.image{
let str = "[img src=\(image.accessibilityIdentifier!)]"
attributeString.beginEditing()
attributeString.(in: range, with: str)
attributeString.endEditing()
}
})
self.attributedText = attributeString
return attributeString.string
}else{
return nil
}
}
Umm.. I solved this problem.
First : Count number of NSTextAttachment
var count = 0
self.attributedText.enumerateAttribute(NSAttachmentAttributeName, in : NSMakeRange(0, self.attributedText.length), options: [], using: { attribute, range, _ in
if let attachment = attribute as? NSTextAttachment,
let image = attachment.image{
count = count + 1
}
})
return count
Second : Replace NSTextAttachment with String and calculate the changed range. <- Repeat
for i in 0..<self.countOfNSTextAttachment(){
let attributedString = NSMutableAttributedString(attributedString: self.attributedText)
var count = 0
attributedString.enumerateAttribute(NSAttachmentAttributeName, in : NSMakeRange(0, attributedString.length), options: [], using: { attribute, range, _ in
if let attachment = attribute as? NSTextAttachment,
let image = attachment.image{
let str = "[img src=\(image.accessibilityIdentifier!)]"
if count == 0{
attributedString.beginEditing()
attributedString.replaceCharacters(in: range, with: NSAttributedString(string : str))
attributedString.endEditing()
self.attributedText = attributedString
}else{
return
}
count = count + 1
}
})
}
return self.attributedText.string
Result : result
Perfect!!
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()