33

Is it possible to change color of single word in UITextView and UITextField ?

If i have typed a word with a symbol infront (eg: @word) , can it's color be changed ?

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
Nithin M Keloth
  • 1,595
  • 1
  • 20
  • 37
  • 3
    [NSAttributedString](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSAttributedString_Class/Reference/Reference.html) and [its usage](http://stackoverflow.com/questions/3482346/how-do-you-use-nsattributedstring). – iDev Jan 09 '13 at 09:34

9 Answers9

67

Yes you need to use NSAttributedString for that, find the RunningAppHere.

Scan through the word and find the range of your word and change its color.

EDIT:

- (IBAction)colorWord:(id)sender {
    NSMutableAttributedString * string = [[NSMutableAttributedString alloc]initWithString:self.text.text];

    NSArray *words=[self.text.text componentsSeparatedByString:@" "];

    for (NSString *word in words) {        
        if ([word hasPrefix:@"@"]) {
            NSRange range=[self.text.text rangeOfString:word];
            [string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:range];           
        }
    }
    [self.text setAttributedText:string];
}

EDIT 2 : see the screenshot enter image description here

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
  • 1
    but how to take that particular word in Textfield into a string ?? – Nithin M Keloth Jan 09 '13 at 09:34
  • Thank you so much Anoop,It changed to red color,But the following words are also in red. :- iphone @Words nithin anoop {in this sentence except iphone,everything is red } – Nithin M Keloth Jan 09 '13 at 11:04
  • actually problem is, when i type---iphone @word then hit the button, and then type again----nithin Anoop; it is also in red color – Nithin M Keloth Jan 09 '13 at 11:22
  • i have uploaded my project in the link... please check there. – Anoop Vaidya Jan 09 '13 at 11:38
  • - Brother i have a doubt,hope you will help me Please check this question - http://stackoverflow.com/questions/14211732/iphone-instragram-integration-issue – Nithin M Keloth Jan 15 '13 at 04:36
  • @AnoopVaidya this solution is wrong, because it affects only the first occurrence of each word – pkacprzak Nov 03 '15 at 05:47
  • @pkacprzak: As per discussion (Single word) with OP, he wanted this only. If you want every words to be changed, if not much work, you could do it easily. – Anoop Vaidya Nov 03 '15 at 07:53
  • Is it possible set color of a particular word in `Storyboard` not from code? – János Jan 27 '17 at 10:13
  • @János: Don't think so. – Anoop Vaidya Jan 27 '17 at 13:56
  • I have a doubt can't we hide @ from @word ? – Mounika Feb 21 '17 at 10:48
  • Hi @Mounika: Yes you can. But according to the question asked, I answered as above. For your question, you can replace `@"@"` with `@""` after the above codes to change the color. Isn't it so simple? – Anoop Vaidya Feb 22 '17 at 03:25
  • @AnoopVaidya my requirement is i want to hide @ pattern while showing text to user & get back while editing text. – Mounika Feb 22 '17 at 04:40
  • @Mounika: You can use delegates to show based on color, or other way is to store both the strings (attributedString with red color without @ and other normal string with @) & do the process. Just a bit of programming logic required to achieve this. – Anoop Vaidya Feb 23 '17 at 05:20
  • @AnoopVaidya Thanks i fixed it :) – Mounika Feb 23 '17 at 05:22
  • Hi @AnoopVaidya your solution is great. Is it also possible to give a word a different background color ? I want to implement something like search tags in ios mail app. For instance when you select a quick filter then the filter appears in the searchbar as a separate component but you can still type ahead to search and also remove this tag by keyboard back button. – user3752049 Dec 30 '17 at 12:12
  • @user3752049: You can do it quite easily, just use `NSBackgroundColorAttributeName` as key and put color in value. – Anoop Vaidya Jan 02 '18 at 13:20
  • @AnoopVaidya Thanks! I figured the color part but couldn't figure out how to add interactive labels/views to searchbar textfield. I'll probably have to create all custom components. Thanks for your help! – user3752049 Jan 03 '18 at 11:43
  • What if our word doesnt have a symbol?! – Reza.Ab Feb 24 '18 at 20:33
  • @Reza.Ab: There should be some distinguishing thing, word, symbol etc so that you can identify what text is needed to change – Anoop Vaidya Feb 26 '18 at 09:17
  • Okay like, If the word im looking forward to change its color is "Closed" it clearly has no symbol, so I cant make its color different? – Reza.Ab Feb 26 '18 at 09:36
  • If you want to make "Closed" you can, just get is location & length and color it. If you want to color only one, or all or some logic you can. *Eventually you have something to identify this word*. – Anoop Vaidya Feb 28 '18 at 06:51
  • @AnoopVaidya this code is working except for the case when the string contains duplicate words prefixing with "@" – cherry_4 Mar 30 '19 at 05:52
6

this is a swift implementation from @Anoop Vaidya answer,this function detect any word between {|myword|} , color these words in red and remove the special characters, hope this may help someone else:

 func getColoredText(text:String) -> NSMutableAttributedString{
    var string:NSMutableAttributedString = NSMutableAttributedString(string: text)
    var words:[NSString] = text.componentsSeparatedByString(" ")

    for (var word:NSString) in words {
        if (word.hasPrefix("{|") && word.hasSuffix("|}")) {
            var range:NSRange = (string.string as NSString).rangeOfString(word)
            string.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: range)
            word = word.stringByReplacingOccurrencesOfString("{|", withString: "")
            word = word.stringByReplacingOccurrencesOfString("|}", withString: "")
            string.replaceCharactersInRange(range, withString: word)
        }
    }
    return string
}

you can use it like this:

self.msgText.attributedText = self.getColoredText("i {|love|} this!")
Fareed Alnamrouti
  • 30,771
  • 4
  • 85
  • 76
5

Modified @fareed's answer for swift 2.0 and this is working (tested in a playground):

func getColoredText(text: String) -> NSMutableAttributedString {
    let string:NSMutableAttributedString = NSMutableAttributedString(string: text)
    let words:[String] = text.componentsSeparatedByString(" ")
    var w = ""

    for word in words {
        if (word.hasPrefix("{|") && word.hasSuffix("|}")) {
            let range:NSRange = (string.string as NSString).rangeOfString(word)
            string.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: range)
            w = word.stringByReplacingOccurrencesOfString("{|", withString: "")
            w = w.stringByReplacingOccurrencesOfString("|}", withString: "")
            string.replaceCharactersInRange(range, withString: w)
        }
    }
    return string
}

getColoredText("i {|love|} this!")
chapani
  • 420
  • 4
  • 14
  • hey, I'm attempting to slightly modify this to work with an array of strings but currently having some difficulties, I was wondering if you could help me out? I asked the question [here](http://stackoverflow.com/q/33472171/5222077) – kye Nov 05 '15 at 16:23
4

@fareed namrouti implementation rewritten in Swift 3

func getColoredText(text: String) -> NSMutableAttributedString {
    let string:NSMutableAttributedString = NSMutableAttributedString(string: text)
    let words:[String] = text.components(separatedBy:" ")
    var w = ""

    for word in words {
        if (word.hasPrefix("{|") && word.hasSuffix("|}")) {
            let range:NSRange = (string.string as NSString).range(of: word)
            string.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: range)
            w = word.replacingOccurrences(of: "{|", with: "")
            w = w.replacingOccurrences(of:"|}", with: "")
            string.replaceCharacters(in: range, with: w)
        }
    }
    return string
}
Eugene Gordin
  • 4,047
  • 3
  • 47
  • 80
1
-(void)colorHashtag
{
NSMutableAttributedString * string = [[NSMutableAttributedString alloc]initWithString:textView.text];

NSString *str = textView.text;
NSError *error = nil;

//I Use regex to detect the pattern I want to change color
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"#(\\w+)" options:0 error:&error];



NSArray *matches = [regex matchesInString:textView.text options:0 range:NSMakeRange(0, textView.text.length)];

for (NSTextCheckingResult *match in matches) {
    NSRange wordRange = [match rangeAtIndex:0];
    [string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:wordRange]; 
}

[textView setAttributedText:string];
}
skittle
  • 248
  • 2
  • 9
0

To expound on Jamal Kharrat's answer, and to rewrite it into SWIFT, here is how to do it in a UITextView:

  1. Set your UITextView to "Attributed" in the storyboard
  2. Right-click & drag to ViewController icon at the top of the view (XC 6), and set the delegate
  3. Create an IBOutlet for your UITextView (we'll call it "textView")
  4. Make your class conform to UITextViewDelegate

Here is Jamal's function written in SWIFT:

func colorHastag(){
    var string:NSMutableAttributedString = NSMutableAttributedString(string: textView.text)
    var str:NSString = textView.text
    var error:NSError?
    var match:NSTextCheckingResult?

    var regEx:NSRegularExpression = NSRegularExpression(pattern: "#(\\w+)", options: nil, error: &error)!
    var matches:NSArray = regEx.matchesInString(textView.text, options: nil, range: NSMakeRange(0, countElements(textView.text)))

    for (match) in matches {
        var wordRange:NSRange = match.rangeAtIndex(0)
        string.addAttribute(NSForegroundColorAttributeName, value: UIColor.blueColor(), range: wordRange)
    }

    textView.attributedText = string
}

Now, you'll need to call this function. To do this every time the user types a character, you can use:

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    self.colorHastag()
    return true
}

You'll notice that I changed the color to blue. You can set it to any color. Also, you can strip out the :Type for every variable. You'll also want to set becomeFirstResponder() and also handle resignFirstResponder() for a good user experience. You could also throw in some error handling. This will only convert hashtags to blue. You will need to modify or add a regEx to handle the @.

AppDever
  • 687
  • 1
  • 7
  • 17
0

The solution is this:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];

NSArray *words=[txtDescription.text componentsSeparatedByString:@" "];

for (NSString *word in words)
{
    if ([word hasPrefix:@"@"] || [word hasPrefix:@"#"])
    {
        [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ ", word]
                                                                                 attributes:@{NSFontAttributeName: [UIFont fontWithName:FONT_LIGHT size:15],
                                                                                              NSForegroundColorAttributeName: [ImageToolbox colorWithHexString:@"f64d5a"]}]];
    }
    else // normal text
    {
        [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ ", word]
                                                                                 attributes:@{NSFontAttributeName: [UIFont fontWithName:FONT_LIGHT size:15],
                                                                                              NSForegroundColorAttributeName: [ImageToolbox colorWithHexString:@"3C2023"]}]];
    }
}

if([[attributedString string] hasSuffix:@" "]) // loose the last space
{
    NSRange lastCharRange;
    lastCharRange.location=0;
    lastCharRange.length=[attributedString string].length-1;

    attributedString=[[NSMutableAttributedString alloc] initWithAttributedString:[attributedString attributedSubstringFromRange:lastCharRange]];
}

[txtDescription setAttributedText:attributedString];
Catalin
  • 1,821
  • 4
  • 26
  • 32
0

Yes it is possible. However I have found it can be a headache trying to use NSMutableAttributesString with a Swift Range. The code below will get you around having to use the Range class and return you an attributed string with the words highlighted a different color.

extension String {
    func getRanges(of string: String) -> [NSRange] {
        var ranges:[NSRange] = []
        if contains(string) {
            let words = self.components(separatedBy: " ")
            var position:Int = 0
            for word in words {
                if word.lowercased() == string.lowercased() {
                    let startIndex = position
                    let endIndex = word.characters.count
                    let range = NSMakeRange(startIndex, endIndex)
                    ranges.append(range)
                }
                position += (word.characters.count + 1) // +1 for space
            }
        }
        return ranges
    }
    func highlight(_ words: [String], this color: UIColor) -> NSMutableAttributedString {
        let attributedString = NSMutableAttributedString(string: self)
        for word in words {
            let ranges = getRanges(of: word)
            for range in ranges {
                attributedString.addAttributes([NSForegroundColorAttributeName: color], range: range)
            }
        }
        return attributedString
    }
}

Usage:

// The strings you're interested in
let string = "The dog ran after the cat"
let words = ["the", "ran"]

// Highlight words and get back attributed string
let attributedString = string.highlight(words, this: .yellow)

// Set attributed string
textView.attributedText = attributedString
Brandon A
  • 8,153
  • 3
  • 42
  • 77
0

After setting an attributedtext you can set typingAttributes of UITextView with the values you want for you input field.

NSDictionary *attribs = @{
    NSForegroundColorAttributeName:[UIColor colorWithHex:kUsernameColor],
    NSFontAttributeName:[UIFont robotoRegularWithSize:40]
};
self.textView.typingAttributes = attribs;
Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69