1

I have a UITextView with many different words next to each other. When user enters that screen I want to start highlighting some words, e.g.:

the first what he sees is a wall of text:

one two three #four five six #seven eight nine ten
eleven #twelve thirteen fourteen fifteen sixteen
some other words #example test whatever #some thing

then, after one second, the word four would change style (color), so he would see:

one two three #FOUR five six #seven eight nine ten
eleven #twelve thirteen fourteen fifteen sixteen
some other words #example test whatever #some thing

then, after one second, another word would highlight (and join already colored four):

one two three #FOUR five six #SEVEN eight nine ten
eleven #twelve thirteen fourteen fifteen sixteen
some other words #example test whatever #some thing

and so on. So after couple seconds user would see:

one two three #FOUR five six #SEVEN eight nine ten
eleven #TWELVE thirteen fourteen fifteen sixteen
some other words #EXAMPLE test whatever #SOME thing

and then the text should stay like this. How can I achieve this?

I thought about looping through words and checking whether they equals the predefined words but I have no idea how to bite it - can you help me with that?

===== EDIT

So to makes things easier for myself I decided to mark highlighted words with # symbol.

I have extension to highlight all words that begin with # in the textView:

extension UITextView {

func formatTextInTextView() {
    self.isScrollEnabled = false
    let selectedRange = self.selectedRange
    let text = self.text
    let font = UIFont(name: "AppleSDGothicNeo-Light", size: 16.0)
    let titleDict: NSDictionary = [NSFontAttributeName: font!]
    // This will give me an attributedString with the desired font
    let attributedString = NSMutableAttributedString(string: text!, attributes: titleDict as! [String : AnyObject])
    let regex = try? NSRegularExpression(pattern: "#(\\w+)", options: [])
    let matches = regex!.matches(in: text!, options: [], range: NSMakeRange(0, (text?.characters.count)!))
    for match in matches {
        let matchRange = match.rangeAt(0)

        let titleDict: NSDictionary = [NSForegroundColorAttributeName: orangeColor]

        attributedString.addAttributes(titleDict as! [String : AnyObject], range: matchRange)
    }
    self.attributedText = attributedString
    self.selectedRange = selectedRange
    self.isScrollEnabled = true
}
}

but I'm not sure how to highlight each word separately with one second delay

user3766930
  • 5,629
  • 10
  • 51
  • 104
  • Your idea of a loop is on the right track. Attempt something. Update your question with some related code showing at least some basic attempt. Clearly describe what you need help with. – rmaddy Nov 09 '16 at 22:55
  • @maddy, I edited my question, thanks for pointing that out! – user3766930 Nov 09 '16 at 23:03
  • That code highlights all words at once due to the regular expression you are using. You need some way to come up with a list (array) of words you wish to highlight then iterate through that array. For each word, you then find all matches of that one word. – rmaddy Nov 09 '16 at 23:05
  • hm I have the list of predefined words, I can store it in a `[string]` table, in this case it would be `["four","seven","twelve","example","some"]`, but how can I proceed from here? – user3766930 Nov 09 '16 at 23:07

1 Answers1

1

Use a timer. Stash matches in a property. Stash the base unhighlighted attributed string in a property. Now have your timer highlight the first match and call itself again in 1 second, highlighting up to the second match and repeat until there are no matches left.

    func highlight (to index: Int = 0) {
        guard index < matches.count else {
            return
        }
        let titleDict: NSDictionary = [NSForegroundColorAttributeName: orangeColor]
        let attributedString = NSMutableAttributedString(attributedString: storedAttributedString)
        for i in 0..< index {
            let matchRange = matches[i].rangeAt(0)
            attributedString.addAttributes(titleDict as! [String : AnyObject], range: matchRange)
        }
        self.attributedText = attributedString
        let _ = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
            self.highlight(to: index + 1)
        }
    }
Josh Homann
  • 15,933
  • 3
  • 30
  • 33
  • thanks Josh, I tried your code, but it brings me lots of errors, check it out http://imgur.com/pOY6UYp do you know how could I fix them? – user3766930 Nov 10 '16 at 00:10
  • 1
    I added the missing argument labels in the example above for NSMutableAttributedString() and for the closure on the timer. Your error on the matches is because matches should be a property and it should be defined as in your code above let matches = regex!.matches(in: text!, options: [], range: NSMakeRange(0, (text?.characters.count)!)) which yields a type of [NSTextCheckingResult] and not [String] – Josh Homann Nov 10 '16 at 00:16
  • Thanks man, two last questions though - this line `self.attributedText = attributedString` throws me an error that `attributedString` is an unresolved identifier :( and the 2nd thing - I had to add `if #available(iOS 10.0, *) {` to the line with Timer - and currently I have an else block with `// Fallback on earlier versions` - what's the best possible option to handle it? can I just somehow highlight all words without a timer then? – user3766930 Nov 10 '16 at 00:34
  • 1
    hoist the lets out of the for loop (see updated answer). If you are not using iOS 10 then use GCD instead of the new timer: DispatchQueue.main.asyncAfter(deadline: .now() + 1) { – Josh Homann Nov 10 '16 at 00:42
  • thank you, now it works :) last question - how can I call this method in viewDidLoad? I mean... what should I put as a parameter? – user3766930 Nov 10 '16 at 00:51
  • You put in 0 as the first parameter so it starts with element 0. you can actually just make 0 the default and then you don't have to pass a parameter. i will edie the answer to reflect. – Josh Homann Nov 10 '16 at 00:53
  • I added a follow up question - http://stackoverflow.com/questions/40531713/how-can-i-change-style-of-pre-selected-words-in-my-textview could you take a look? – user3766930 Nov 10 '16 at 17:27