46

How can you detect a change of data in a UITextView with Swift? The following code does not do any detection.

I am declaring the UITextView :

@IBOutlet weak var bodyText: UITextView!

optional func textViewDidChange(_ textView: UITextView!) {
    println(bodyText.text)
}

Thanks Scott

Kampai
  • 22,848
  • 21
  • 95
  • 95
user3896519
  • 515
  • 1
  • 4
  • 8
  • This UITextView placeholder solution provides a working example: http://stackoverflow.com/questions/27652227/text-view-placeholder-swift/28271069#28271069 – clearlight Feb 02 '17 at 02:01

6 Answers6

113

You need to set UITextView delegate and implement textViewDidChange: method in it. Unfortunately, I do not know if swift documentation is available online. All the links go to the objective-c documentation.

The code will look like this: (updated for SWIFT 4.2)

class ViewController: UIViewController, UITextViewDelegate { //If your class is not conforms to the UITextViewDelegate protocol you will not be able to set it as delegate to UITextView

    @IBOutlet weak var bodyText: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        bodyText.delegate = self //Without setting the delegate you won't be able to track UITextView events
    }

    func textViewDidChange(_ textView: UITextView) { //Handle the text changes here
        print(textView.text); //the textView parameter is the textView where text was changed
    }
}
zeeshan
  • 4,913
  • 1
  • 49
  • 58
Sergey Pekar
  • 8,555
  • 7
  • 47
  • 54
6

For my case, I wanted the implementation to be independent from a UIViewController, so i dont need to assign a delegate just for text changes. Or even maybe there is some kind of validation on the UITextView, and you wanna contain it per field instead of a delegate managing a lot of complicated logic.

It requires to subclass UITextView, but its very much worth it imo:

class TextView: UITextView {

    convenience init() {
        self.init(frame: CGRect.zero, textContainer: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(textDidChangeNotification), name: UITextView.textDidChangeNotification , object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func textDidChangeNotification(_ notif: Notification) {
        guard self == notif.object as? UITextView else {
            return
        }
        textDidChange()
    }

    func textDidChange() {
        // the text in the textview just changed, below goes the code for whatever you need to do given this event

        // or you can just set the textDidChangeHandler closure to execute every time the text changes, useful if you want to keep logic out of the class
        textDidChangeHandler?()
    }

    var textDidChangeHandler: (()->Void)?

}
Lucas Chwe
  • 2,578
  • 27
  • 17
5

Set delegate of UITextView.Refer UITextViewDelegate

Write this in viewDidLoad

bodyText!.delegate = self
codester
  • 36,891
  • 10
  • 74
  • 72
4

For Swift 4:

func textViewDidChange(_ textView: UITextView) {
  // Your code here
}
Andy
  • 829
  • 6
  • 11
3

For Swift 4.2, you can add UITextViewTextDidChange, UITextViewTextDidBeginEditing & UITextViewTextDidEndEditing notification observers to a UITextView like this:

let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(textViewDidChange), name: NSNotification.Name.UITextViewTextDidChange , object: nil)
nc.addObserver(self, selector: #selector(textViewDidBeginEditing), name: NSNotification.Name.UITextViewTextDidBeginEditing , object: nil)
nc.addObserver(self, selector: #selector(textViewDidEndEditing), name: NSNotification.Name.UITextViewTextDidEndEditing , object: nil)

And the observers look like this:

@objc func textViewDidChange(_ notif: Notification) {
    guard notif.object is UITextView else { return }
    // Do something
}

@objc func textViewDidBeginEditing(_ notif: Notification) {
    guard notif.object is UITextView else { return }
    // Do something
}

@objc func textViewDidEndEditing(_ notif: Notification) {
    guard notif.object is UITextView else { return }
    // Do something
}

For Swift5, the format of the 'name' values are UITextView.textDidChangeNotification.

Fattie
  • 27,874
  • 70
  • 431
  • 719
JTODR
  • 318
  • 2
  • 10
0

When subclassing UITextView:

import UIKit
class SelfAwareTextView: UITextView {
    
    func setup() {
        NotificationCenter.default.addObserver(self,
         selector: #selector(change),
         name: UITextView.textDidChangeNotification,
         object: self)
    }
    
    @objc func change(_ notif: Notification) {
        print("the user edited the text. It's now: " + text)
    }
}

In add observor, the object: is the sending object.

Somewhat confusingly, you set that to self since, in fact, we simply want notifications from ourself; we don't care about other text views.

Regarding setup, recall that (in general) when subclassing a UITextView, you want to do setup in awakeFromNib rather than init, etc, since, among other issues you then catch any IBInspectable values.

Fattie
  • 27,874
  • 70
  • 431
  • 719