3

I have a text view in my app which allows the user to type anything in it. I want the text view to detect a few words when they are typed and change their colour.

For example, if the user types- "My favourite colour is red", then I want the word 'red' to get the text colour 'red'. But thus should happen immediately after the word red is typed in the sentence. How should I implement this?

8
  • I see you nested those classes inside the function. Don't do that. When you create/design classes you are creating templates. So you want to have them in a separate file. Inside your UIViewController you create an instance of that class and call the functions off that class. Commented Sep 3, 2015 at 15:14
  • @RMenke Ok, I created a Swift file with the class code. Then I typed the other code in the ViewController class. How should I create and instance and call the functions of that class? Commented Sep 3, 2015 at 15:27
  • syntaxHighlighter: Highlighter! does not work Commented Sep 3, 2015 at 15:28
  • creating an instance : var myInstanceOfAClass : AClass = AClass() or var myInstanceOfAClass : AClass = AClass(stuffTheClassNeeds: StuffIGiveToTheClass) Look closely at the code in my answer. You need all parts. Commented Sep 3, 2015 at 15:30
  • calling a class function : myInstanceOfAClass.myClassFunction() or myInstanceOfAClass.myClassFunction(stuffTheFunctionNeeds: StuffIGiveToTheFunction) Commented Sep 3, 2015 at 15:35

5 Answers 5

5

You need three things :

  • In the interface builder you need to use one of the delegate methods. I think "editingChanged" is the best here. Drag from the textfield with "ctrl" to you document.

  • You need a way to go over the string from textfield and detect words.

    componentsSeparatedByCharactersInSet

  • You need a way to style one word in a sentence.

    NSMutableAttributedString

Also interesting : how to separate a string without removing the delimiter

Part of the code (throw this in a playground) :

This will find all the words to change, but remove separators. More work is needed to complete the code.

var strSeperator : NSCharacterSet = NSCharacterSet(charactersInString: ",.:;?! ") var strArray = str.componentsSeparatedByCharactersInSet(strSeperator) var styledText = NSMutableAttributedString() for i in 0..<strArray.count { let currentWord = strArray[i] var currentBoolFoundInControl : Bool = false for iB in 0..<colorTheseStrings.count { let controlWord = colorTheseStrings[iB] if controlWord == currentWord { currentBoolFoundInControl = true // add to mutable attributed string in a color } } var attrString1 = NSMutableAttributedString(string: "\(strArray[i]) ") if currentBoolFoundInControl == true { var range = (strArray[i] as NSString).rangeOfString(strArray[i]) if strArray[i] == "red" { attrString1.addAttribute(NSForegroundColorAttributeName, value: UIColor(red: 0.5, green: 0.0, blue: 0.0, alpha: 1.0), range: range) } else if strArray[i] == "blue" { attrString1.addAttribute(NSForegroundColorAttributeName, value: UIColor(red: 0.0, green: 0.0, blue: 0.5, alpha: 1.0), range: range) } } styledText.appendAttributedString(attrString1) } myTextField.attributedText = styledText 

One More Way:

download it here

I think this way is the most robust and the coolest and the most efficient. Code could be cleaned up a bit and could use a bit more style/flair. This does have trouble with things like the "sky blue" because it will find "blue" first and replace it. But I had to leave some room for improvement. ;)

And I couldn't help myself, so I fixed that last issue.

And again, I fixed, my fix. More efficient and actually correct now.

The first part is the HighLighter class, drop this in a separate document.

// text hightlighter class SyntaxGroup { var wordCollection : [String] = [] var type : String = "" var color : UIColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) init(wordCollection_I : [String], type_I : String, color_I: UIColor) { wordCollection = wordCollection_I type = type_I color = color_I } } class SyntaxDictionairy { var collections : [SyntaxGroup] = [] } class SyntaxRange { var range : NSRange var color : UIColor init (color_I : UIColor, range_I : NSRange) { color = color_I range = range_I } } class HighLighter { var ranges : [SyntaxRange] = [] var baseString : NSMutableString = NSMutableString() var highlightedString : NSMutableAttributedString = NSMutableAttributedString() var syntaxDictionairy : SyntaxDictionairy init (syntaxDictionairy_I : SyntaxDictionairy) { syntaxDictionairy = syntaxDictionairy_I } func run(string : String?, completion: (finished: Bool) -> Void) { ranges = [] // reset the ranges, else it crashes when you change a previous part of the text highlightedString = NSMutableAttributedString() // make sure all strings start fresh baseString = NSMutableString() // make sure all strings start fresh // multi threading to prevent locking up the interface with large libraries. let qualityOfServiceClass = QOS_CLASS_DEFAULT let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0) dispatch_async(backgroundQueue) { () -> Void in if string != nil && string != "" { self.highlightedString = NSMutableAttributedString(string: string!) for i in 0..<self.syntaxDictionairy.collections.count { for iB in 0..<self.syntaxDictionairy.collections[i].wordCollection.count { let currentWordToCheck = self.syntaxDictionairy.collections[i].wordCollection[iB] self.baseString = NSMutableString(string: string!) while self.baseString.containsString(self.syntaxDictionairy.collections[i].wordCollection[iB]) { let nsRange = (self.baseString as NSString).rangeOfString(currentWordToCheck) let newSyntaxRange = SyntaxRange(color_I: self.syntaxDictionairy.collections[i].color, range_I: nsRange) self.ranges.append(newSyntaxRange) var replaceString = "" for _ in 0..<nsRange.length { replaceString += "§" // secret unallowed character } self.baseString.replaceCharactersInRange(nsRange, withString: replaceString) } } } for i in 0..<self.ranges.count { self.highlightedString.addAttribute(NSForegroundColorAttributeName, value: self.ranges[i].color, range: self.ranges[i].range) } } dispatch_sync(dispatch_get_main_queue()) { () -> Void in completion(finished: true) } } } } 

In the ViewController : This is the code you will use with the UITextfield. First create an instance of the highlighter and set up your dictionary of words and corresponding colours. Then start the run function when needed.

import UIKit class ViewController: UIViewController { @IBOutlet weak var myTextfield: UITextField! var syntaxHighLighter : HighLighter! // declare highlighter override func viewDidLoad() { super.viewDidLoad() setUpHighLighter() } // this is just a little function to put the set up for the highlighter outside of viewDidLoad() func setUpHighLighter() { // build a dict of words to highlight let redColor = UIColor(red: 0.5, green: 0.0, blue: 0.0, alpha: 1.0) let blueColor = UIColor(red: 0.0, green: 0.0, blue: 0.5, alpha: 1.0) let greenColor = UIColor(red: 0.0, green: 0.5, blue: 0.0, alpha: 1.0) let redGroup = SyntaxGroup(wordCollection_I: ["red","bordeaux"], type_I: "Color", color_I: redColor) let blueGroup = SyntaxGroup(wordCollection_I: ["coralblue","blue","skyblue","azur"], type_I: "Color", color_I: blueColor) let greenGroup = SyntaxGroup(wordCollection_I: ["green"], type_I: "Color", color_I: greenColor) let dictionairy : SyntaxDictionairy = SyntaxDictionairy() dictionairy.collections.append(blueGroup) dictionairy.collections.append(greenGroup) dictionairy.collections.append(redGroup) syntaxHighLighter = HighLighter(syntaxDictionairy_I: dictionairy) } // this is where the magic happens, place the code from inside the editingChanged inside your function that responds to text changes. @IBAction func editingChanged(sender: UITextField) { syntaxHighLighter.run(myTextfield.text) { (finished) -> Void in self.myTextfield.attributedText = self.syntaxHighLighter.highlightedString } } } 
Sign up to request clarification or add additional context in comments.

1 Comment

Comments are not for extended discussion; this conversation has been moved to chat.
3
textField.addTarget(self, action: "textFieldDidChange:",forControlEvents: UIControlEvents.EditingChanged) func textFieldDidChange(textField: UITextField) { //your code var main_string = textField.text var string_to_color = "red" var range = (main_string as NSString).rangeOfString(string_to_color) var attributedString = NSMutableAttributedString(string:main_string) attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor() , range: range) } 

13 Comments

I got that, but how should I change the colour of one specific word only? @FawadMasud
You should use NSMutableattributedstring. I will update shortly.
or check my answer to have it now ;)
damn that is short. A combination of both our answers is best. You also need a way to detect multiple strings.
@RMenke I think same procedure will do for multiple strings.
|
1

maybe you can implement the Text View Delegate to detect when the user finish typing, ask in a conditional if the written text is what you want, and in the positive case do something(change colour). Here you have some documentation https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextViewDelegate_Protocol/ You can try implementing something like this:

let text: UITextView? //this must be binded with the storyboard ui //MARK UITextViewDelegate func textViewDidChange(_ textView: UITextView){ if (textView.text == "bla"){ textView.colour == UIColour(Red) } } 

This code is just a schema. Read the docs and check the syntaxis. Hope it helps!!

Fede

2 Comments

Looking at the code I can say that this won't work at all as the word can be anywhere in middle of the sentence :)
In that case you must parse the textView.text and search for the content in any part of the sentence.
0

You have to use attributedText in the UITextView delegate method

- (void)textViewDidChange:(UITextView *)textView 

this will solve your problem.

Comments

0

I think what you should do is this (Correct me if I'm wrong because I don't have Xcode to testit on this computer but this should work). If that doesn't work do what Fawad Masud and add this to view did load...

 yourTextView.addTarget(self, action: "textViewDidChange:",forControlEvents: UIControlEvents.EditingChanged) 

override func viewDidLoad() { super.viewDidLoad() yourTextView.delegate = self //Basically makes the function below applicable to this textview } func textViewDidChange(textView: UITextView) { //Every time text changes this code is run var textViewText = yourTextView.text as NSString! if textViewText.containsString("red") { textViewText.textColor = UIColor.redColor() } } 

1 Comment

I am unable to add .addTarget on the textView @EthanCotler

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.