I wrote this mid method in Extension.
func mid(fromIndex: Int, toIndex: Int) -> String {
var _fromIndex = 0
if( fromIndex >= self.count ) {
return ""
}
else if( fromIndex > 0 ){
_fromIndex = fromIndex
}
var _toIndex = 0
if( toIndex >= self.count ) {
_toIndex = self.count - 1
}
else if( toIndex > 0 ){
_toIndex = toIndex
}
if( _fromIndex > _toIndex ){
return ""
}
return String(self[self.index(self.startIndex, offsetBy: _fromIndex)...self.index(self.startIndex, offsetBy: _toIndex)])
}
And use this method to retrieve user deleted string.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
defer{
if text.isEmpty {
let deletedText = textView.attributedText.string.mid(fromIndex: range.lowerBound, toIndex: range.upperBound)
}
}
}
But it sometimes throws this exception.
Fatal Exception: NSRangeException
*** -[NSBigMutableString characterAtIndex:]: Index 672 out of bounds; string length 672
specialized String.subscript.getter
I already check the size of the string before using String.subscript.getter.
if( toIndex >= self.count ) {
_toIndex = self.count - 1
}
And the text operation should be on the main thread. Why am I still getting this exception? Did I misunderstood the string index in Swift?
UPDATE1
According to @Wattholm's answer.
I'm using this way to get text with NSRange from UITextView.
let contents: String = textView.text
if let _range = Range(range, in: contents) {
let deletedText = String(contents[_range])
}
else {
logHelper.w("Failed to get deletedText from NSRange, try UITextRange.")
if let startPosition = textView.position(from: textView.beginningOfDocument, offset: range.location),
let endPosition = textView.position(from: startPosition, offset: range.length),
let textRange = textView.textRange(from: startPosition, to: endPosition),
let deletedText = textView.text(in: textRange) {
// do something with deletedText
}
else {
logHelper.w("Failed to get deletedText from UITextRange, try NSString.")
let deletedText = (contents as NSString).mid(fromIndex: range.lowerBound, toIndex: range.upperBound)
}
}
But it still fall into the second else and throwing NSRangeException.
Which means the length and the index still doesn't match each other.
UPDATE2
Tried to use NSString.substring but still keep getting this exception.
let deletedText = (contents as NSString).substring(with: range)
Maybe there's a bug in UITextView?
deletedText = contents.mid()- did you meantextView.textinstead ofcontents?toIndexisself.count + 17for example.textView.attributedText.string.