NSRange から Range へ 質問する

NSRange から Range へ 質問する

Swift でNSRangeに変換するにはどうすればいいですか?Range<String.Index>

次のUITextFieldDelegate方法を使用したいと思います。

    func textField(textField: UITextField!,
        shouldChangeCharactersInRange range: NSRange,
        replacementString string: String!) -> Bool {

textField.text.stringByReplacingCharactersInRange(???, withString: string)

ここに画像の説明を入力してください

ベストアンサー1

Swift 4 (Xcode 9)以降、Swift 標準ライブラリには、Swift 文字列範囲 ( Range<String.Index>) とNSString範囲 ( NSRange) を変換するメソッドが用意されています。例:

let str = "a��b����c"
let r1 = str.range(of: "����")!

// String range to NSRange:
let n1 = NSRange(r1, in: str)
print((str as NSString).substring(with: n1)) // ����

// NSRange back to String range:
let r2 = Range(n1, in: str)!
print(str[r2]) // ����

したがって、テキストフィールドデリゲートメソッドでのテキスト置換は次のように実行できます。

func textField(_ textField: UITextField,
               shouldChangeCharactersIn range: NSRange,
               replacementString string: String) -> Bool {

    if let oldString = textField.text {
        let newString = oldString.replacingCharacters(in: Range(range, in: oldString)!,
                                                      with: string)
        // ...
    }
    // ...
}

(Swift 3 以前の古い回答:)

Swift 1.2ではString.Index初期化子がある

init?(_ utf16Index: UTF16Index, within characters: String)

NSRangeこれを使用すると、中間の への変換を行わずに に正しく変換できますRange<String.Index>(絵文字、地域指標、その他の拡張書記素クラスターのすべてのケースを含む) NSString

extension String {
    func rangeFromNSRange(nsRange : NSRange) -> Range<String.Index>? {
        let from16 = advance(utf16.startIndex, nsRange.location, utf16.endIndex)
        let to16 = advance(from16, nsRange.length, utf16.endIndex)
        if let from = String.Index(from16, within: self),
            let to = String.Index(to16, within: self) {
                return from ..< to
        }
        return nil
    }
}

このメソッドは、指定された Swift 文字列に対してすべての が有効であるとは限らないため、オプションの文字列範囲を返します。NSRange

デリゲートUITextFieldDelegateメソッドは次のように記述できます。

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

    if let swRange = textField.text.rangeFromNSRange(range) {
        let newString = textField.text.stringByReplacingCharactersInRange(swRange, withString: string)
        // ...
    }
    return true
}

逆変換は

extension String {
    func NSRangeFromRange(range : Range<String.Index>) -> NSRange {
        let utf16view = self.utf16
        let from = String.UTF16View.Index(range.startIndex, within: utf16view) 
        let to = String.UTF16View.Index(range.endIndex, within: utf16view)
        return NSMakeRange(from - utf16view.startIndex, to - from)
    }
}

簡単なテスト:

let str = "a��b����c"
let r1 = str.rangeOfString("����")!

// String range to NSRange:
let n1 = str.NSRangeFromRange(r1)
println((str as NSString).substringWithRange(n1)) // ����

// NSRange back to String range:
let r2 = str.rangeFromNSRange(n1)!
println(str.substringWithRange(r2)) // ����

Swift 2 のアップデート:

Swift 2版は、rangeFromNSRange()Serhii Yakovenkoによってすでに提供されている。この答え完全を期すためにここに記載します:

extension String {
    func rangeFromNSRange(nsRange : NSRange) -> Range<String.Index>? {
        let from16 = utf16.startIndex.advancedBy(nsRange.location, limit: utf16.endIndex)
        let to16 = from16.advancedBy(nsRange.length, limit: utf16.endIndex)
        if let from = String.Index(from16, within: self),
            let to = String.Index(to16, within: self) {
                return from ..< to
        }
        return nil
    }
}

Swift 2版NSRangeFromRange()

extension String {
    func NSRangeFromRange(range : Range<String.Index>) -> NSRange {
        let utf16view = self.utf16
        let from = String.UTF16View.Index(range.startIndex, within: utf16view)
        let to = String.UTF16View.Index(range.endIndex, within: utf16view)
        return NSMakeRange(utf16view.startIndex.distanceTo(from), from.distanceTo(to))
    }
}

Swift 3 (Xcode 8) のアップデート:

extension String {
    func nsRange(from range: Range<String.Index>) -> NSRange {
        let from = range.lowerBound.samePosition(in: utf16)
        let to = range.upperBound.samePosition(in: utf16)
        return NSRange(location: utf16.distance(from: utf16.startIndex, to: from),
                       length: utf16.distance(from: from, to: to))
    }
}

extension String {
    func range(from nsRange: NSRange) -> Range<String.Index>? {
        guard
            let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
            let to16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location + nsRange.length, limitedBy: utf16.endIndex),
            let from = from16.samePosition(in: self),
            let to = to16.samePosition(in: self)
            else { return nil }
        return from ..< to
    }
}

例:

let str = "a��b����c"
let r1 = str.range(of: "����")!

// String range to NSRange:
let n1 = str.nsRange(from: r1)
print((str as NSString).substring(with: n1)) // ����

// NSRange back to String range:
let r2 = str.range(from: n1)!
print(str.substring(with: r2)) // ����

おすすめ記事