Reputation: 1544
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?
Upvotes: 3
Views: 1966
Reputation: 8391
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:
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
}
}
}
Upvotes: 5
Reputation: 21
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
Upvotes: 1
Reputation: 12344
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)
}
Upvotes: 3
Reputation: 57
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()
}
}
Upvotes: 0
Reputation: 11127
You have to use attributedText in the UITextView delegate method
- (void)textViewDidChange:(UITextView *)textView
this will solve your problem.
Upvotes: 0