1

I've a textfield zone which is supposed to have a flexible height, growing along number of lines you have written.

I would like to listen to it's height's change and then when a new line will be create this listener will say "Hi the height has changed !".

Initial Height

---------- Initial Height ----------

Height has changed !

---------- Height has changed !!! ----------

I can't find out a proper way to do this, because addObserver in swift require a NotificationIdentifier like keyboardWillShow and here I don't have one like heightWillChange.

How can I call a function when the height changed ?

Bram'in
  • 618
  • 1
  • 7
  • 18

3 Answers3

1

Use delegate method of your UITextView

var textViewHeight: CGFloat = 0

func textViewDidChange(textView: UITextView)
{
    let newTextViewHeight = textView.frame.size.height
    if textViewHeight != newTextViewHeight
    {
        print("---------- Height has changed !!! ----------")
    }
    textViewHeight = newTextViewHeight
}
Yury
  • 6,044
  • 3
  • 19
  • 41
  • Here I use this technic but it causes a problem. Indeed my textView's height behaviour is ruled by an other class and this class is called after the textViewDidChange. Then, for one change, height changes but too late to be catch by textViewDidChange. – Bram'in Jun 27 '16 at 15:49
  • @Bram'in if so, you can dive inside your custom class and find place, where all changes ends. You may fork repository if it is github, or even make pull request with didChangeHeight functionality. – Yury Jun 27 '16 at 16:05
1

You can use KVO. If you're doing this from a view controller you can add observing in viewDidLoad. For example:

    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))

        self.textField.addObserver(self, forKeyPath: "frame", options: .New, context: nil)
    }

Then respond in:

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if keyPath == "frame" && object === self.textField
        {
            print(self.textField.frame)
        }
    }

Then remove observing when your view controller is deinited:

deinit {
    self.textField.removeObserver(self, forKeyPath: "frame")
}
beyowulf
  • 15,101
  • 2
  • 34
  • 40
  • Thank you for your answer. I've tried your solution but it still doesn't work. The observer notice anything. I've made some research and what I can found out is that an observer is quitly complicate to declare. This solution appears to be the best but should be modified. I'll look at this tomorrow. – Bram'in Jun 28 '16 at 01:07
0
extension MyThing: UITextViewDelegate {
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        guard let oldText = textView.text else {
            return true
        }
        let newText = (textView.text as NSString).replacingCharacters(in: range, with: text)

        let size = CGSize(width: textView.bounds.width, height: CGFloat.greatestFiniteMagnitude)
        let font = textView.font!

        let oldTextRect = newText.boundingRect(with: size,
                                               options: .usesLineFragmentOrigin,
                                               attributes: [.font : font],
                                               context: nil)
        let newTextRect = oldText.boundingRect(with: size,
                                               options: .usesLineFragmentOrigin,
                                               attributes: [.font : font],
                                               context: nil)
        if oldTextRect != newTextRect {
            print("Text height changed")
        } else {
            print("Text height didnt change :(")
        }
        return true
    }
}