咱們仍然在談論Swift字符串的主題,今天咱們將會看到你在使用String
和處理時遇到的問題NSString
。若是您使用來自AppKit和Foundation的文本API,則必須處理這兩種字符串類型,而且須要注意一些棘手的差別。swift
咱們將使用一個小示例應用程序來播放字符串中的字符串和範圍。咱們要看的第一件事是Swift字符串和NSStrings的範圍之間的差別。安全
當咱們輸入搜索字段時,咱們想在文本視圖中突出顯示找到的單詞。視圖控制器已經有一個方法,search
咱們能夠在其中使用text
爲搜索字段的文本提供的屬性String
:bash
func search(_ searchTerm: String) {
guard let range = text.range(of: searchTerm) else { return }
}
複製代碼
在Swift字符串中找到範圍後,咱們但願使用方法showFindIndicator(for charRange: NSRange)
on 在文本視圖中突出顯示此範圍NSTextView
。不幸的是,咱們不能直接將咱們傳遞Range
給這個方法 - 咱們應該先用新的NSRange
初始化器轉換它:ui
func search(_ searchTerm: String) {
guard let range = text.range(of: searchTerm) else { return }
let nsRange = NSRange(range, in: text)
textView.showFindIndicator(for: nsRange)
}
複製代碼
咱們運行應用程序並看到它的工做原理:在搜索字段中輸入「Hello」後,文本視圖中的「Hello」字樣閃爍。若是輸入「World」,它也會這樣作。spa
這是一個簡單的解決方案,但若是您不使用正確的NSRange
初始化程序,則很容易出錯。咱們在Stack Overflow上看到了不少代碼,在其餘地方人們手動計算索引來構造NSRange
:3d
func search(_ searchTerm: String) {
guard let range = text.range(of: searchTerm) else { return }
let location = text.distance(from: text.startIndex, to: range.lowerBound)
let length = text.distance(from: range.lowerBound, to: range.upperBound)
let nsRange = NSRange(location: location, length: length)
textView.showFindIndicator(for: nsRange)
}
複製代碼
04:09這段代碼編譯,可是錯了。咱們一開始可能不會注意到,由於若是咱們搜索「你好」,正確的單詞會閃爍,但若是咱們搜索「世界」,第一個表情符號會突出顯示!code
Swift絃樂和NSStrings對角色的概念有不一樣的概念; 咱們不能經過簡單地計算索引Range
來轉換爲a NSRange
,由於它們具備不一樣的索引類型。具備膚色修飾符的女性消防員表情符號可能看起來像單個字符,但它不適用於NSString。cdn
咱們恢復正確的代碼並繼續咱們的應用程序的第二個功能。blog
當咱們選擇文本的一部分時,咱們但願在右側的信息面板中顯示有關該選擇的一些信息。此次,咱們必須將NSRange
文本視圖轉換爲a Range
,以便從Swift字符串中獲取子字符串text
並將其傳遞給名爲的標籤selection
:索引
func updateInfo() {
let nsRange = textView.selectedRange()
let range = Range(nsRange, in: text)!
let value = String(text[range])
selection.stringValue = value
}
複製代碼
咱們能夠運行應用程序並檢查正確的文本是否顯示爲選擇。一樣,使用正確的範圍轉換初始化器很重要,由於不然咱們會遇到與之前相同的問題,但此次是在另外一個方向:從。NSRange
到Range
。
既然咱們有正確的字符串,咱們可使用有關選擇的信息填充其餘標籤:
func updateInfo() {
let nsRange = textView.selectedRange()
let range = Range(nsRange, in: text)!
let value = String(text[range])
selection.stringValue = value
characterCount.stringValue = String(value.count)
nsStringCount.stringValue = String((value as NSString).length)
utf16Count.stringValue = String(value.utf16.count)
unicodeScalarCount.stringValue = String(value.unicodeScalars.count)
byteCount.stringValue = String(value.data(using: .utf8)!.count)
}
複製代碼
這會產生一些有趣的信息。若是咱們再次選擇「Hello」,則信息看起來很簡單,由於Swift字符串和NSString都是五個字符長,Swift字符串的其餘視圖也是如此:
若是咱們選擇👩🏻🚒表情符號,事情會變得更加複雜。正如預期的那樣,Swift字符串是一個字符長。NSString表示它的長度爲7 - 這與Swift字符串utf16
視圖的長度匹配,由於NSStrings由UTF-16支持:
Swift字符串的unicodeScalars
視圖返回四個計數。您能夠在線查看Unicode字符的元素。在這種狀況下,消防員表情符號字符由「女人」,膚色修飾符,零寬度木匠和「消防引擎」組成 - 總共有四個標量。
法國國旗表情符號🇫🇷被列爲一個Swift字符串字符,四個NSString字符和兩個Unicode標量(特殊標誌版本的「F」和「R」)。考慮全部這些不一樣的計數,混合範圍和指數是行不通的。若是你只處理簡單的字母字符,你可使用Range
和NSRange
互換。可是隻要你遇到一個更復雜的角色,好比表情符號,如下索引在各類字符串視圖中都會有所不一樣。
家庭角色也頗有趣。咱們使用的表情符號👨👩👧由三個標尺組成,用於我的,另外還有兩個零寬度鏈接器將它們組合成一個系列:總共五個標量。
在對字符串的原始數據進行操做時,咱們必需要當心,由於咱們可能會更改單個系列成員的字節並沒有意中更改整個系列字符。對整個字符進行操做比對單個字節或標量進行操做更安全。幸運的是,咱們今天看到的兩個初始化器,轉換Range
爲NSRange
和返回,使事情變得更容易。