nsstring - nsrange string



NSRange 범위에서<String.Index> (9)

@ 에밀리 (Emilie)의 위대한 답에 대한 리프. 교체 / 경쟁 대답이 아닙니다.
(Xcode6-Beta5)

var original    = "🇪🇸😂This is a test"
var replacement = "!"

var startIndex = advance(original.startIndex, 1) // Start at the second character
var endIndex   = advance(startIndex, 2) // point ahead two characters
var range      = Range(start:startIndex, end:endIndex)
var final = original.stringByReplacingCharactersInRange(range, withString:replacement)

println("start index: \(startIndex)")
println("end index:   \(endIndex)")
println("range:       \(range)")
println("original:    \(original)")
println("final:       \(final)")

산출:

start index: 4
end index:   7
range:       4..<7
original:    🇪🇸😂This is a test
final:       🇪🇸!his is a test

여러 코드 단위에 대한 인덱스 계정을 확인하십시오. 플래그 (REGIONAL INDICATOR SYMBOL LETTERS ES)는 8 바이트이고 (FACE WITH TEARS OF JOY)는 4 바이트입니다. (이 경우 UTF-8, UTF-16 및 UTF-32 표현에서 바이트 수는 동일하다는 것을 알 수 있습니다.)

그것을 func로 래핑하기 :

func replaceString(#string:String, #with:String, #start:Int, #length:Int) ->String {
    var startIndex = advance(original.startIndex, start) // Start at the second character
    var endIndex   = advance(startIndex, length) // point ahead two characters
    var range      = Range(start:startIndex, end:endIndex)
    var final = original.stringByReplacingCharactersInRange(range, withString: replacement)
    return final
}

var newString = replaceString(string:original, with:replacement, start:1, length:2)
println("newString:\(newString)")

산출:

newString: !his is a test

https://ffff65535.com

Swift에서 NSRangeRange<String.Index> 로 변환하려면 어떻게해야합니까?

다음 UITextFieldDelegate 메서드를 사용하고 싶습니다.

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

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


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

var oldString = textfield.text!
let newRange = oldString.startIndex.advancedBy(range.location)..<oldString.startIndex.advancedBy(range.location + range.length)
let newString = oldString.stringByReplacingCharactersInRange(newRange, withString: string)

가장 깨끗한 swift2 솔루션은 NSRange에서 카테고리를 만드는 것입니다.

extension NSRange {
    func stringRangeForText(string: String) -> Range<String.Index> {
        let start = string.startIndex.advancedBy(self.location)
        let end = start.advancedBy(self.length)
        return Range<String.Index>(start: start, end: end)
    }
}

텍스트 입력란 위임 함수를 호출합니다.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let range = range.stringRangeForText(textField.text)
    let output = textField.text.stringByReplacingCharactersInRange(range, withString: string)

    // your code goes here....

    return true
}

기존 NSRange 대신 Range<String.Index> 를 사용해야합니다. 내가하는 방식 (어쩌면 더 좋은 방법이있을 수있다)은 문자열의 String.Index 를 가져 와서 이동하는 것입니다.

나는 당신이 대체하려고하는 범위를 모르지만, 처음 2자를 대체하고 싶다고 가정 해 봅시다.

var start = textField.text.startIndex // Start at the string's start index
var end = advance(textField.text.startIndex, 2) // Take start index and advance 2 characters forward
var range: Range<String.Index> = Range<String.Index>(start: start,end: end)

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

마틴 R에 의한 이 대답 은 유니 코드를 설명하기 때문에 정확하다고 보입니다.

그러나 게시물 (Swift 1)에서 Swift 2.0 (Xcode 7)에서는 advance() 함수를 제거했기 때문에 코드가 컴파일되지 않습니다. 업데이트 된 버전은 다음과 같습니다.

스위프트 2

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
    }
}

스위프트 3

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

스위프트 4

extension String {
    func rangeFromNSRange(nsRange : NSRange) -> Range<String.Index>? {
        return Range(nsRange, in: self)
    }
}

이것은 NSRangeRange<String.Index> 로 변환하는 방법을 구체적으로 묻기 때문에 Emilie의 대답과 비슷합니다. 다음과 같이하면됩니다 :

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

     let start = advance(textField.text.startIndex, range.location) 
     let end = advance(start, range.length) 
     let swiftRange = Range<String.Index>(start: start, end: end) 
     ...

}

replacingCharacters(in: NSRange, with: NSString)NSString 버전 (Swift String과 반대 replacingCharacters(in: NSRange, with: NSString)NSRange 허용하므로 간단한 해결책은 StringNSString 먼저 변환하는 것 입니다. 위임자와 교체 방법 이름은 Swift 3과 Swift에서 약간 씩 다르기 때문에 사용하는 Swift에 따라 다릅니다.

스위프트 3.0

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

  let nsString = textField.text as NSString?
  let newString = nsString?.replacingCharacters(in: range, with: string)
}

스위프트 2.x

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

    let nsString = textField.text as NSString?
    let newString = nsString?.stringByReplacingCharactersInRange(range, withString: string)
}

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)
        // ...
    }
    // ...
}

(스위프트 3 이전에 대한 이전 답변 :)

Swift 1.2 현재, String.Index 에는 이니셜 라이저가 있습니다.

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

NSString 을 중간 변환하지 않고 NSRangeRange<String.Index> (Emojis, Regional Indicators 또는 기타 확장 된 grapheme 클러스터의 모든 경우 포함)로 올바르게 변환하는 데 사용할 수 있습니다.

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
    }
}

이 메서드는 모든 NSRange 가 주어진 Swift 문자열에 유효하지 않기 때문에 선택적 문자열 범위를 반환합니다.

그런 다음 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)) // 🇩🇪

스위프트 2 업데이트 :

rangeFromNSRange() 의 Swift 2 버전은 이미이 답변 에서 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
    }
}

NSRangeFromRange() 의 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)) // 🇩🇪

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

       let strString = ((textField.text)! as NSString).stringByReplacingCharactersInRange(range, withString: string)

 }