SirRupertIII
SirRupertIII

Reputation: 12595

Is there a delegate call for when the text is changed on a UITextView?

When I set my UITextView programmatically like this:

[self.textView setText:@""];

The delegate method textViewDidChange: does not get called. Is there a way I can find that without making a UITextView subclass?

Upvotes: 14

Views: 9824

Answers (8)

Will Buffington
Will Buffington

Reputation: 1700

Swift 5 solution... If you made it here looking to implement a running word count as the user is typing in a UITextView, as I was, you can use the following:

First, add your UITextViewDelegate to your class name:

class YourClassName: UIViewController, UITextViewDelegate {

Then in your viewDidLoad() set the delegate as self:

tvMyTextView.delegate = self

Finally, add the delegate method for textViewDidChange:

func textViewDidChange(_ textView: UITextView) {
    let str = tvMyTextView.text
    let components = str!.components(separatedBy: .whitespacesAndNewlines)
    let words = components.filter { !$0.isEmpty }
    lblWordCount.text = "Word Count So Far: \(words.count)"
}

In my delegate method, I am counting the actual words, not characters. As the user types, the word count is continually updated and shown in a label I have added to my View named 'lblWordCount'. Hope this helps someone out there!

Upvotes: 0

AndyW
AndyW

Reputation: 1094

In swift you could override the text variable from the UITextView class:

class MyTextView: UITextView {

    override public var text: String? {
        didSet {
            self.textViewDidChange(self)
        }
    }

}

Upvotes: 4

rebello95
rebello95

Reputation: 8576

When manually setting the text of a UITextView with code, the textViewDidChange: method does not get called. (If you have your text view's delegate set, it will get called when the user edits it, though.)

One possible workaround would be to manually call textViewDidChange: anytime you edit the text. For example:

[self.textView setText:@""];
[self textViewDidChange:self.textView];

Kind of a hackish way of doing it, but it gets the job done.

Upvotes: 31

Konsol Labapen
Konsol Labapen

Reputation: 2434

I upvoted @rebello95's response because it is one approach. But another, less hacky approach is to do as

- (void)whereIManuallyChangeTextView
{//you don't actually have to create this method. It's simply wherever you are setting the textview to empty
  [self.textView setText:@""];
  [self respondToChangeInTextView:self.textView];
}
    
- (void)textViewDidChange:(UITextView *)textView
{
  //...some work and then
  [self respondToChangeInTextView:textView];
}
    
- (void)respondToChangeInTextView:(UITextView *)textView
{
  //what you want to happen when you programmatically/manually or interactively change the textview
}

This snippet exemplifies a respectable pattern that will make your code more readable.

Upvotes: 9

Andreas
Andreas

Reputation: 161

Old post, but I had the same problem and thought I would share my solution (in Swift).

textViewDidChange(_ textView: UITextView) does not get called by just setting the text property, but it does get called when using replace(range: UITextRange, withText: String). So you need to create a UITextRange for the entire string of the UITextView and replace it with a new string.

// Create a range of entire string
let textRange = textView.textRange(from: textView.beginningOfDocument, to: textView.endOfDocument)
// Create a new string
let newText = ""
// Call Replace the string in your textView with the new string
textView.replace(textRange!, withText: newText)

That should do it. Of course, you need to set up UITextViewDelegate for this to work:

class ViewController: UIViewController, UITextViewDelegate {

Upvotes: 2

LiLi Kazine
LiLi Kazine

Reputation: 223

delegate method textDidChange does not respond to programmatical changes in textes, you can use observation to get notified

  1. declare your text view variable with @objc dynamic
  2. declare and hold a variable with type NSKeyValueObservation
  3. use function observe(_:changeHandler:) bind your text view's text property, hold the return value with variable declared in step 2
  4. observe changes in changeHandler

example:

@objc dynamic private var textView: UITextView!
private var observation: NSKeyValueObservation?

func bind() {
    observation = observe(\.textView.text, options: [.old, .new]) { object, change in
        print(object, change)
    }
}

Upvotes: 0

Youdong Zhang
Youdong Zhang

Reputation: 1

You could also subclass UITextView and override setText to include

[self textViewDidChange:self.textView]

so that you don't have to call it every time you set the text of your UITextView.

Upvotes: 0

user2387149
user2387149

Reputation: 1218

use this instead: (this won't reset the current text)

[self.textView insertText:@"something"];

this will call the delegate and will add text where the cursor is. Of course if you want to reset the whole text you can either:

[self.textView setText:@""];
[self textViewDidChange:self.textView];

or

[self.textView setText:@""];
[self.textView insertText:@"something"];

Upvotes: -1

Related Questions