Ios NSRangeException by String.subscript.getter

Ios NSRangeException by String.subscript.getter,ios,swift,Ios,Swift,我在Extension中编写了这个mid方法 func mid(fromIndex: Int, toIndex: Int) -> String { var _fromIndex = 0 if( fromIndex >= self.count ) { return "" } else if( fromIndex > 0 ){ _fromIndex = fromIndex } var _toInd

我在
Extension
中编写了这个
mid
方法

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)])
}
并使用此方法检索用户删除的字符串

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)
        }
    }
}
但它有时会抛出这个例外

let deletedText = (contents as NSString).substring(with: range)
致命异常:NSRangeException
***-[NSBigMutableString characterAtIndex:]:索引672超出范围;字符串长度672
专用字符串.subscript.getter

在使用
string.subscript.getter
之前,我已经检查了字符串的大小

if( toIndex >= self.count ) {
    _toIndex = self.count - 1
}
文本操作应该在主线程上。 为什么我仍然得到这个例外? 我是否误解了Swift中的字符串索引

更新1

根据@Wattholm的回答。 我使用这种方法从
UITextView
获取带有
NSRange
的文本

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)
    }
}
但它仍然会落入第二个
else
和抛出
NSRangeException
。 这意味着长度和索引仍然不匹配

更新2

尝试使用
NSString.substring
,但仍不断出现此异常

let deletedText = (contents as NSString).substring(with: range)

也许UITextView中存在错误?

我可能错了,但我认为这里的问题与String和NSString之间或Range和NSRange之间的固有差异有关。字符串的计数并不总是与NSString的长度相同

可能有一种更简单的方法,但我尝试的是以与为String创建扩展相同的方式为NSString创建扩展,然后将“contents”(我假设是字符串;这里似乎缺少一些代码,因为“contents”没有在任何地方声明)强制转换为NSString,以便它能够正常工作,由于textview为我们提供了NSRange:

extension NSString {
    func mid(fromIndex: Int, toIndex: Int) -> NSString {

        var _fromIndex = 0

        if( fromIndex >= self.length ) {
            return ""
        }
        else if( fromIndex > 0 ){
            _fromIndex = fromIndex
        }

        var _toIndex = 0

        if( toIndex >= self.length ) {
            _toIndex = self.length - 1
        }
        else if( toIndex > 0 ){
            _toIndex = toIndex
        }

        if( _fromIndex > _toIndex ){
            return ""
        }

        return NSString(string: self.substring(with: NSRange(location: _fromIndex, length: _toIndex - _fromIndex + 1)))
    }
}
对于文本视图:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    defer{
        if text.isEmpty {
            let deletedText = (contents as NSString).mid(fromIndex: range.lowerBound, toIndex: range.upperBound - 1)
        }
    }
}
另一种方法是将NSRange转换为一个范围,这可能是更好的方法,尤其是当您要求字符计数与字符串在Swift中的字符计数完全相同时。textview函数中的范围:NSRange可以转换为如下范围:

设swiftRange=Range(范围,单位:rangeText)


其中rangeText应替换为range所引用的text属性。

我可能错了,但我认为这里的问题与String和NSString之间或range和NSRange之间的固有差异有关。字符串的计数并不总是与NSString的长度相同

可能有一种更简单的方法,但我尝试的是以与为String创建扩展相同的方式为NSString创建扩展,然后将“contents”(我假设是字符串;这里似乎缺少一些代码,因为“contents”没有在任何地方声明)强制转换为NSString,以便它能够正常工作,由于textview为我们提供了NSRange:

extension NSString {
    func mid(fromIndex: Int, toIndex: Int) -> NSString {

        var _fromIndex = 0

        if( fromIndex >= self.length ) {
            return ""
        }
        else if( fromIndex > 0 ){
            _fromIndex = fromIndex
        }

        var _toIndex = 0

        if( toIndex >= self.length ) {
            _toIndex = self.length - 1
        }
        else if( toIndex > 0 ){
            _toIndex = toIndex
        }

        if( _fromIndex > _toIndex ){
            return ""
        }

        return NSString(string: self.substring(with: NSRange(location: _fromIndex, length: _toIndex - _fromIndex + 1)))
    }
}
对于文本视图:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    defer{
        if text.isEmpty {
            let deletedText = (contents as NSString).mid(fromIndex: range.lowerBound, toIndex: range.upperBound - 1)
        }
    }
}
另一种方法是将NSRange转换为一个范围,这可能是更好的方法,尤其是当您要求字符计数与字符串在Swift中的字符计数完全相同时。textview函数中的范围:NSRange可以转换为如下范围:

设swiftRange=Range(范围,单位:rangeText)

其中rangeText应替换为该范围所指的text属性。

这是一个更快速的版本,它利用了从
NSRange
range
的可靠转换,反之亦然

extension String {

    func mid(fromIndex: Int, toIndex: Int) -> String {
        guard toIndex >= fromIndex else  { return "" }
        let nsRange = NSRange(location: fromIndex, length: toIndex + 1 - fromIndex)
        guard let range = Range(nsRange, in: self) else { return "" }
        return String(self[range])
    }
}
实际上,您不需要扩展,因为您已经有了一个
NSRange
,只需编写

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    defer{
        if text.isEmpty {
            let textViewString = textView.attributedText.string
            guard let swiftRange = Range(range, in: textViewString) else { return }
            let deletedText = String(textViewString[swiftRange])
        }
    }
}
这是一个更快速的版本,它利用了从
NSRange
Range
的可靠转换,反之亦然

extension String {

    func mid(fromIndex: Int, toIndex: Int) -> String {
        guard toIndex >= fromIndex else  { return "" }
        let nsRange = NSRange(location: fromIndex, length: toIndex + 1 - fromIndex)
        guard let range = Range(nsRange, in: self) else { return "" }
        return String(self[range])
    }
}
实际上,您不需要扩展,因为您已经有了一个
NSRange
,只需编写

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    defer{
        if text.isEmpty {
            let textViewString = textView.attributedText.string
            guard let swiftRange = Range(range, in: textViewString) else { return }
            let deletedText = String(textViewString[swiftRange])
        }
    }
}

deletedText=contents.mid()
-你是说
textView.text
而不是
contents
?还要注意
toIndex
self.count+17
的情况。对不起,我忘了提到内容来自
textView.AttributeText.string
。发生错误时,您是否可以在textView中提供字符串文本,以便我可以复制错误?我还想知道,在NSRangeException发生之前,用户是如何触发对ShouldChangeTextIn的调用的?如果您可以将一些数据记录到控制台,例如下限、上限、内容、删除文本,也可能会发生这种情况。
deletedText=contents.mid()
-您是指
textView.text
而不是
contents
?请注意
toIndex
self.count+17
的情况。例如:?@Yonat抱歉,我忘了提到内容来自
textView.AttributeText.string
。发生错误时,您是否可以在textView中提供字符串文本,以便我可以复制错误?我还想知道,在NSRangeException发生之前,用户是如何触发对ShouldChangeTextIn的调用的?如果您可以将一些数据记录到控制台,例如下限、上限、内容、删除的文本,也可以这样做。
contents
来自
textView.attributedText.string
。我已经编辑了代码段。
内容
来自
textView.attributedText.string
。我已经编辑了代码段。我已经在我的应用程序中使用了
Range(Range,in:textViewString)
。您可以在UPDATE1中看到这一行。但它仍然落在第二个
else
中。可能范围与内容不匹配?我已经在我的应用程序中使用了
range(range,in:textViewString)
。您可以在UPDATE1中看到这一行。但它仍然落在第二个
else
中。可能范围与内容不匹配?