ThiagoAM
ThiagoAM

Reputation: 1542

How to get the Character that is before the cursor in a UITextView?

Let's say I have the following UITextView object:

var textView = UITextView()
textView.text = "Hello World!"

Now let's say I don't want to allow the user to delete the "W" character while editing it. How could I know which character is before the cursor (or selected by it)?

I'm looking for something that would work like this:

if textView.characterBeforeCursor() != "W" {
   textView.deleteBackward()
}

or... (when the user selects the "W" character):

if textView.selectedTextContains("W") == false {
   textView.deleteBackward()
}

What approach should I use to accomplish this?

Upvotes: 3

Views: 2578

Answers (2)

ielyamani
ielyamani

Reputation: 18581

This should do it:

let forbiddenLetter = "W" 

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    guard let txt = textView.text, let txtRange = Range(range, in: txt) else {
        return false
    }

    let subString: Substring = txt[txtRange]

    return !subString.contains(forbiddenLetter)
}

In the code above let txt = textView.text is just for simplicity, we could keep force-unwrapping textView.text! since the .text property is designed never returns nil for a non-nil UITextView.

By let txtRange = Range(range, in: txt) we get a variable of type Range<String.Index> instead of the vanilla NSRange that range is. By doing so we can get the Substring of txt that the textView is about to change.

Finally, the result of checking whether or not the subString contains the forbiddenLetter, is returned.


This snippet would prevent deleting W by using:

  • Backspace key
  • Deleting selection
  • Pasting over selection
  • Autocorrect (from the popup)

Upvotes: 1

Woodstock
Woodstock

Reputation: 22926

Here's an idea, not fully tested, but seems to work... Just grab the character about to be acted upon and block backspace if its the target... Also with regard to selection of text, if the selection contains the target at all, we block new text.

import UIKit

class ViewController: UIViewController, UITextViewDelegate {

    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.textView.delegate = self
        // Do any additional setup after loading the view, typically from a nib.
    }

    func characterBeforeCursor() -> String? {
        // get the cursor position
        if let cursorRange = textView.selectedTextRange {
            // get the position one character before the cursor start position
            if let newPosition = textView.position(from: cursorRange.start, offset: -1) {
                let range = textView.textRange(from: newPosition, to: cursorRange.start)
                return textView.text(in: range!)
            }
        }
        return nil
    }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if (characterBeforeCursor() == "W") {
            let char = text.cString(using: String.Encoding.utf8)!
            let isBackSpace = strcmp(char, "\\b")
            if (isBackSpace == -92) {
                return false
            }
            return true
        }
        else {
            if let range = textView.selectedTextRange {
                let selectedText = textView.text(in: range)
                if (selectedText!.contains("W")) {
                    return false
                }
            }
            return true
        }
    }
}

Upvotes: 4

Related Questions