在Objective-c中,我们使用NSRange创建范围

NSRange range;

那么如何在Swift中创建范围呢?

推荐答案

Updated for Swift 4

Swift 射程比NSRange更复杂,在Swift 3中也没有变得更简单.如果你想try 理解这种复杂性背后的原因,请阅读thisthis.我将向您展示如何创建它们以及何时可以使用它们.

Closed Ranges: a...b

range operator创建了一个Swift范围,其中包括元素aand和元素b,即使b是类型(如Int.max)的最大可能值.有两种不同类型的闭合范围:ClosedRangeCountableClosedRange.

1. ClosedRange

Swift中所有范围的要素都具有可比性(即,它们符合可比协议).允许您从集合访问范围内的元素.下面是一个例子:

let myRange: ClosedRange = 1...3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]

然而,ClosedRange是不可计数的(即,它不符合序列协议).这意味着不能用for循环迭代元素.为此,你需要CountableClosedRange.

2. CountableClosedRange

这与上一个类似,只是现在范围也可以迭代.

let myRange: CountableClosedRange = 1...3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]

for index in myRange {
    print(myArray[index])
}

Half-Open Ranges: a..<b

该范围运算符包括元素a,但不包括元素b.如上所述,有两种不同类型的半开放范围:RangeCountableRange.

1. Range

ClosedRange一样,您可以使用Range访问集合的元素.例子:

let myRange: Range = 1..<3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]

不过,你不能重复Range,因为它只是可比较的,而不是可跨越的.

2. CountableRange

CountableRange允许迭代.

let myRange: CountableRange = 1..<3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]

for index in myRange {
    print(myArray[index])
}

NSRange

在Swift中,您有时仍然可以(必须)使用NSRange(例如,在制作attributed strings时),因此了解如何制作NSRange是很有帮助的.

let myNSRange = NSRange(location: 3, length: 2)

请注意,这是位置和length,而不是开始索引和结束索引.这里的示例与Swift 3..<5的含义类似.然而,由于类型不同,它们不能互换.

Ranges with Strings

.....<范围运算符是创建范围的简写方法.例如:

let myRange = 1..<3

创建相同范围的长期方法是

let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3

你可以看到这里的索引类型是Int.不过,这对String不起作用,因为字符串是由字符组成的,而且并非所有字符的大小都相同.(更多信息请阅读this.)表情符号?, 例如,比字母"b"占用更多空间.

Problem with NSRange

try 用表情符号NSRangeNSString,你就会明白我的意思.头痛

let myNSRange = NSRange(location: 1, length: 3)

let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"

let myNSString2: NSString = "a?cde"
myNSString2.substring(with: myNSRange) // "?c"    Where is the "d"!?

smiley face需要存储两个UTF-16代码单元,因此它给出了不包含"d"的意外结果.

Swift Solution

因此,对于Swift字符串,您使用Range<String.Index>,而不是Range<Int>.字符串索引是基于特定字符串计算的,这样它就知道是否存在任何表情符号或扩展的字形集群.

实例

var myString = "abcde"
let start = myString.index(myString.startIndex, offsetBy: 1)
let end = myString.index(myString.startIndex, offsetBy: 4)
let myRange = start..<end
myString[myRange] // "bcd"

myString = "a?cde"
let start2 = myString.index(myString.startIndex, offsetBy: 1)
let end2 = myString.index(myString.startIndex, offsetBy: 4)
let myRange2 = start2..<end2
myString[myRange2] // "?cd"

One-sided Ranges: a... and ...b and ..<b

在Swift 4中,事情被简化了一点.无论何时可以推断范围的起点或终点,都可以将其禁用.

Int

可以使用单边整数范围来迭代集合.下面是documentation人中的一些例子.

// iterate from index 2 to the end of the array
for name in names[2...] {
    print(name)
}

// iterate from the beginning of the array to index 2
for name in names[...2] {
    print(name)
}

// iterate from the beginning of the array up to but not including index 2
for name in names[..<2] {
    print(name)
}

// the range from negative infinity to 5. You can't iterate forward
// over this because the starting point in unknown.
let range = ...5
range.contains(7)   // false
range.contains(4)   // true
range.contains(-1)  // true

// You can iterate over this but it will be an infinate loop 
// so you have to break out at some point.
let range = 5...

String

这也适用于字符串范围.如果你正在做一个一端有str.startIndexstr.endIndex的范围,你可以把它关掉.编译器将推断出它.

鉴于

var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)

let myRange = ..<index    // Hello

您可以使用...从索引转到str.endIndex

var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index...        // playground

See also:

Notes

  • 不能使用一个字符串在另一个字符串上创建的范围.
  • 正如您所看到的,字符串范围在Swift中是一个难题,但它们确实可以更好地处理表情符号和其他Unicode标量.

Further Study

Swift相关问答推荐

在Swift中initt()完成后运行一个函数

在`NavigationStack`中使用`SafeAreaInset`修饰符时出现SwiftUI异常行为

按下一步后无法让文本字段切换焦点

将NavigationSplitView更改为NavigationStack

Swift 扩展:通过方法重载增强现有类

使第二个视图变灰 SwiftUI

Swift Random Float Fatal Error:在无限范围内没有均匀分布

Firebase removeObserver 在 Swift 5 中不起作用

需要将 json 映射到 Swift 中的模型

关于变量的视图的 SwiftUI 生命周期

如果 Swift 无法分配内存会怎样?

`let case(value)` 和 `case(let value)` 之间的区别 (Swift)

引用类型(类)不需要但 struct 需要的可识别 id

UICollectionView 自定义单元格在 Swift 中填充宽度

由于编译器中的内部保护级别,无法访问框架 init 中的公共 struct

如何在今天的扩展(iOS)中访问 CoreData 模型

如何将多个枚举值作为函数参数传递

如何快速从另一个视图控制器重新加载表格视图

tableView 部分标题消失 SWIFT

iPhone 纵向和 iPad 横向的通用应用程序