问题在于,尽管
String.Index
符合
Comparable
协议,但您仍需要指定要使用的
Range类型
public struct Range<Bound> where Bound : Comparable {}
注意:由于
NSString
使用UTF-16,请检查
this以及您所引用的
链接,您的初始代码
对于由多个UTF-16代码点组成的字符无法正确工作。以下是适用于Swift 3的更新工作版本。
extension Range where Bound == String.Index {
init(_ range: NSRange, in string: String) {
let lower16 = string.utf16.index(string.utf16.startIndex, offsetBy: range.location)
let upper16 = string.utf16.index(string.utf16.startIndex, offsetBy: NSMaxRange(range))
if let lower = lower16.samePosition(in: string),
let upper = upper16.samePosition(in: string) {
self.init(lower..<upper)
} else {
fatalError("init(range:in:) could not be implemented")
}
}
}
let string = "❄️Let it snow! ☃️"
let range1 = NSRange(location: 0, length: 1)
let r1 = Range<String.Index>(range1, in: string)
let range2 = NSRange(location: 1, length: 2)
let r2 = Range<String.Index>(range2, in: string)
回答OP的评论:问题在于
一个NSString对象编码为符合Unicode标准的文本字符串,表示为UTF-16代码单元序列。组成字符串内容的Unicode标量值可以长达21位。较长的标量值可能需要两个UInt16值进行存储。
因此,一些字母(如❄️)在NSString中占用两个UInt16值,但在String中只占用一个。当您将NSRange参数传递给初始化程序时,您可能希望它在NSString中正常工作。
在我的示例中,将string
转换为utf16后,r1
和r2
的结果分别为'❄️'和致命错误。与此同时,您原始解决方案的结果分别为'❄️L'和'Le'。希望您能看到这种差异。
如果您坚持不转换为utf16的解决方案,可以查看Swift源代码来做出决定。在Swift 4中,您将拥有内置库作为初始化器。代码如下。
extension Range where Bound == String.Index {
public init?(_ range: NSRange, in string: String) {
let u = string.utf16
guard range.location != NSNotFound,
let start = u.index(u.startIndex, offsetBy: range.location, limitedBy: u.endIndex),
let end = u.index(u.startIndex, offsetBy: range.location + range.length, limitedBy: u.endIndex),
let lowerBound = String.Index(start, within: string),
let upperBound = String.Index(end, within: string)
else { return nil }
self = lowerBound..<upperBound
}
}