web-dev-qa-db-ja.com

SwiftにおけるString.Indexの機能

私はSwift 3で私の古いコードと答えの一部を更新してきましたが、私がSwift StringsとIndexingに到達したとき、物事を理解するのは大変でした。

具体的には、次のことを試していました。

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error

2行目で次のエラーが発生した

'advancedBy'は使用できません。インデックスをnステップ進めるには、インデックスを生成したCharacterViewインスタンスで 'index(_:offsetBy :)'を呼び出します。

Stringには以下のメソッドがあります。

str.index(after: String.Index)
str.index(before: String.Index)
str.index(String.Index, offsetBy: String.IndexDistance)
str.index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

最初は本当に混乱していたので、理解するまで遊んでみました。それらがどのように使用されているかを示すために、以下に回答を追加します。

85
Suragch

enter image description here

以下の例はすべて使用しています

var str = "Hello, playground"

startIndexおよびendIndex

  • startIndexは最初の文字のインデックスです
  • endIndexはインデックスですafter最後の文字。

// character
str[str.startIndex] // H
str[str.endIndex]   // error: after last character

// range
let range = str.startIndex..<str.endIndex
str[range]  // "Hello, playground"

Swift 4's 片側範囲 を使用すると、範囲は次のいずれかの形式に簡略化できます。

let range = str.startIndex...
let range = ..<str.endIndex

わかりやすくするために、次の例では完全な形式を使用しますが、読みやすくするために、コードでは片側の範囲を使用することをお勧めします。

after

index(after: String.Index)のように

  • afterは、与えられたインデックスの直後の文字のインデックスを参照します。

// character
let index = str.index(after: str.startIndex)
str[index]  // "e"

// range
let range = str.index(after: str.startIndex)..<str.endIndex
str[range]  // "Ello, playground"

before

index(before: String.Index)のように

  • beforeは、与えられたインデックスの直前の文字のインデックスを参照します。

// character
let index = str.index(before: str.endIndex)
str[index]  // d

// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range]  // Hello, playgroun

offsetBy

index(String.Index, offsetBy: String.IndexDistance)のように

  • offsetByの値は正または負にすることができ、指定されたインデックスから始まります。タイプはString.IndexDistanceですが、Intを指定できます。

// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index]  // p

// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
str[range]  // play

limitedBy

index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)のように

  • limitedByは、オフセットによってインデックスが範囲外にならないようにするために役立ちます。これはバウンディングインデックスです。オフセットが制限を超える可能性があるので、このメソッドはOptionalを返します。インデックスが範囲外の場合はnilを返します。

// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
    str[index]  // p
}

オフセットが77ではなく7であれば、ifステートメントはスキップされます。

なぜString.Indexが必要なのですか?

文字列にIntインデックスを使うほうがmuch簡単です。すべての文字列に対して新しいString.Indexを作成しなければならないのは、Swiftの文字がボンネットの下ですべて同じ長さではないためです。単一のSwift Characterは、1つ、2つ、またはそれ以上のUnicodeコードポイントで構成されることがあります。したがって、各一意の文字列はその文字のインデックスを計算する必要があります。

この複雑さをIntインデックス拡張の背後に隠すことはおそらく可能ですが、私はそうすることを躊躇しています。実際に起こっていることを思い出すのは良いことです。

219
Suragch

TableViewController内にUITextViewを作成します。私は関数textViewDidChangeを使い、それからreturn-key-inputをチェックしました。それからreturn-key-inputを検出した場合、returnキーの入力を削除してキーボードを消します。

func textViewDidChange(_ textView: UITextView) {
    tableView.beginUpdates()
    if textView.text.contains("\n"){
        textView.text.remove(at: textView.text.index(before: textView.text.endIndex))
        textView.resignFirstResponder()
    }
    tableView.endUpdates()
}
0
Karl Mogensen