14

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?

rebello95
  • 8,486
  • 5
  • 44
  • 65
SirRupertIII
  • 12,324
  • 20
  • 72
  • 121

7 Answers7

31

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.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
rebello95
  • 8,486
  • 5
  • 44
  • 65
  • It's an interesting approach. Mostly, people don't consider calling delegate methods of frameworks manually. However, it's a nice idea. – Onur Tuna Jun 26 '19 at 06:43
  • Definitely not hackish, IMO. Methods are meant to be called and this one is staring you right in the face. – trndjc Jul 03 '20 at 19:21
  • I think that it's safer to use `[self.textView.delegate textViewDidChange:self.textView]`, in case the `delegate` changes to a different object than `self`. – Iulian Onofrei Jul 28 '22 at 08:07
8

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.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
Konsol Labapen
  • 2,424
  • 2
  • 15
  • 12
4

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

class MyTextView: UITextView {

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

}
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
AndyW
  • 985
  • 3
  • 10
  • 21
2

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 {
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
Andreas
  • 151
  • 2
  • 9
0

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.

0

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)
    }
}
LiLi Kazine
  • 195
  • 2
  • 9
-1

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"];
user2387149
  • 1,219
  • 16
  • 28
  • insertText doesn't call the delegate. Also: "textViewDidChange: Discussion The text view calls this method in response to user-initiated changes to the text. This method is not called in response to programmatically initiated changes." via: https://developer.apple.com/reference/uikit/uitextviewdelegate#jumpTo_7 – OhadM Nov 16 '16 at 08:51