How to highlight a UITextView's text line by line in swift?

后端 未结 1 1764
难免孤独
难免孤独 2021-02-06 18:05

I am trying to highlight line by line the text in a UITextView. I want to iterate over each line and highlight that one for the user to see, then I want to remove t

相关标签:
1条回答
  • 2021-02-06 18:17

    So you want this:

    You need the text view's contents to always be the full string, with one line highlighted, but your code sets it to just the highlighted line. Your code also schedules all the highlights to happen at the same time (.now() + 0.5) instead of at different times.

    Here's what I'd suggest:

    1. Create an array of ranges, one range per line.

    2. Use that array to modify the text view's textStorage by removing and adding the .backgroundColor attribute as needed to highlight and unhighlight lines.

    3. When you highlight line n, schedule the highlighting of line n+1. This has two advantages: it will be easier and more efficient to cancel the animation early if you need to, and it will be easier to make the animation repeat endlessly if you need to.

    I created the demo above using this playground:

    import UIKit
    import PlaygroundSupport
    
    let text = "This is\n some placeholder\n text\nwith newlines."
    let textView = UITextView(frame: CGRect(x: 0, y:0, width: 200, height: 100))
    textView.backgroundColor = .white
    textView.text = text
    
    let textStorage = textView.textStorage
    
    // Use NSString here because textStorage expects the kind of ranges returned by NSString,
    // not the kind of ranges returned by String.
    let storageString = textStorage.string as NSString
    var lineRanges = [NSRange]()
    storageString.enumerateSubstrings(in: NSMakeRange(0, storageString.length), options: .byLines, using: { (_, lineRange, _, _) in
        lineRanges.append(lineRange)
    })
    
    func setBackgroundColor(_ color: UIColor?, forLine line: Int) {
        if let color = color {
            textStorage.addAttribute(.backgroundColor, value: color, range: lineRanges[line])
        } else {
            textStorage.removeAttribute(.backgroundColor, range: lineRanges[line])
        }
    }
    
    func scheduleHighlighting(ofLine line: Int) {
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
            if line > 0 { setBackgroundColor(nil, forLine: line - 1) }
            guard line < lineRanges.count else { return }
            setBackgroundColor(.yellow, forLine: line)
            scheduleHighlighting(ofLine: line + 1)
        }
    }
    
    scheduleHighlighting(ofLine: 0)
    
    PlaygroundPage.current.liveView = textView
    
    0 讨论(0)
提交回复
热议问题