Sourav Gupta's solution partially worked for me however it had a few pitfalls, namely that it didn't correctly call the target code if the editing happened in the middle of the text body (for example: writing a bunch of text, moving the cursor to the middle and then hitting return three times) as well as handling insertion events like pasting and text correction. As a result, I did some testing on my own and found a few more conditions in which we can assume that editing has moved to the next line.
note: this approach has a few false positives but I found that to be more tolerable as I use this for relaying out the code and so laying out unnecessarily is far better than not responding
Swift 5
private var previousRect:CGRect = CGRect.zero
private func checkIfReturnOrLineWrap(textView:UITextView) {
let currentRect = textView.caretRect(for:textView.endOfDocument)
if (currentRect.origin.y != previousRect.origin.y && (previousRect.origin.y != CGFloat.infinity || currentRect.origin.y > 0) ||
currentRect.origin.y == CGFloat.infinity) {
//React to the line change!
}
previousRect = currentRect
}
//MARK: UITextViewDelegate methods (be sure to hook up your textviews!)
func textViewDidChange(_ textView: UITextView) {
checkIfReturnOrLineWrap(textView: textView)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
checkIfReturnOrLineWrap(textView: textView)
return true
}