wake-up-neo.net

NSRange von Swift Range?

Problem: NSAttributedString nimmt einen NSRange, während ich einen Swift-String verwende, der Range verwendet

let text = "Long paragraph saying something goes here!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})

Erzeugt den folgenden Fehler:

fehler: 'Range' kann nicht in 'NSRange' .__ konvertiert werden. attributedString.addAttribute (NSForegroundColorAttributeName, Wert: NSColor.redColor (), Bereich: substringRange)

126
Jay

Swift String-Bereiche und NSString-Bereiche sind nicht "kompatibel" . Zum Beispiel ein Emoji wie ???? zählt als ein schnelles Zeichen, aber als zwei NSString Zeichen (ein sogenanntes UTF-16-Ersatzpaar).

Ihre vorgeschlagene Lösung führt daher zu unerwarteten Ergebnissen, wenn die Zeichenfolge Solche Zeichen enthält. Beispiel:

let text = "????????????Long paragraph saying!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
    let start = distance(text.startIndex, substringRange.startIndex)
    let length = distance(substringRange.startIndex, substringRange.endIndex)
    let range = NSMakeRange(start, length)

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: range)
    }
})
println(attributedString)

Ausgabe:

 ????????????? Long paragra {
} Ph sagen {
 NSColor = "NSCalibratedRGBColorSpace 1 0 0 1"; 
} Ing! {
} 

Wie Sie sehen, wurde "ph say" mit dem Attribut und nicht mit "say" gekennzeichnet.

Da NS(Mutable)AttributedString letztendlich eine NSString und eine NSRange erfordert, ist es __. Besser, den angegebenen String zuerst in NSString zu konvertieren. Dann ist substringRange eine NSRange und Sie müssen die Bereiche nicht mehr konvertieren:

let text = "????????????Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: nsText)

nsText.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})
println(attributedString)

Ausgabe:

 ????????????? Langer Absatz {
} Sagt {
 NSColor = "NSCalibratedRGBColorSpace 1 0 0 1"; 
}! {
} 

Update für Swift 2:

let text = "????????????Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstringsInRange(textRange, options: .ByWords, usingBlock: {
    (substring, substringRange, _, _) in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})
print(attributedString)

Update für Swift 3:

let text = "????????????Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstrings(in: textRange, options: .byWords, using: {
    (substring, substringRange, _, _) in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.red, range: substringRange)
    }
})
print(attributedString)

Update für Swift 4:

Ab Swift 4 (Xcode 9) bietet die Swift-Standardbibliothek .__ eine Methode zum Konvertieren zwischen Range<String.Index> und NSRange. Eine Konvertierung in NSString ist nicht mehr erforderlich:

let text = "????????????Long paragraph saying!"
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstrings(in: text.startIndex..<text.endIndex, options: .byWords) {
    (substring, substringRange, _, _) in
    if substring == "saying" {
        attributedString.addAttribute(.foregroundColor, value: NSColor.red,
                                      range: NSRange(substringRange, in: text))
    }
}
print(attributedString)

Hier ist substringRange ein Range<String.Index> und wird mit .__ in die entsprechende NSRange umgewandelt

NSRange(substringRange, in: text)
212
Martin R

In Fällen wie dem von Ihnen beschriebenen habe ich festgestellt, dass dies funktioniert. Es ist relativ kurz und süß:

 let attributedString = NSMutableAttributedString(string: "follow the yellow brick road") //can essentially come from a textField.text as well (will need to unwrap though)
 let text = "follow the yellow brick road"
 let str = NSString(string: text) 
 let theRange = str.rangeOfString("yellow")
 attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellowColor(), range: theRange)
45
royherma

Mögliche Lösung

Swift stellt Abstand () bereit, der den Abstand zwischen Anfang und Ende misst, der zum Erstellen eines NS-Bereichs verwendet werden kann:

let text = "Long paragraph saying something goes here!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
    let start = distance(text.startIndex, substringRange.startIndex)
    let length = distance(substringRange.startIndex, substringRange.endIndex)
    let range = NSMakeRange(start, length)

//    println("Word: \(substring) - \(d1) to \(d2)")

        if (substring == "saying") {
            attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: range)
        }
})
10
Jay

Die Antworten sind gut, aber mit Swift 4 könnten Sie Ihren Code ein wenig vereinfachen:

let text = "Test string"
let substring = "string"

let substringRange = text.range(of: substring)!
let nsRange = NSRange(substringRange, in: text)

Seien Sie vorsichtig, da das Ergebnis der range-Funktion entpackt werden muss.

9

Swift 4:

Klar, ich weiß, dass Swift 4 bereits eine Erweiterung für NSRange hat 

public init<R, S>(_ region: R, in target: S) where R : RangeExpression,
    S : StringProtocol, 
    R.Bound == String.Index, S.Index == String.Index

Ich weiß, dass diese Initiation in den meisten Fällen ausreicht. Siehe seine Verwendung:

let string = "Many animals here: ???????????? !!!"

if let range = string.range(of: "????????????"){
     print((string as NSString).substring(with: NSRange(range, in: string))) //  "????????????"
 }

Die Konvertierung kann jedoch direkt von Range <String.Index> nach NSRange ohne die String-Instanz von Swift durchgeführt werden.

Anstelle der generischen Verwendung von init, für die der Parameter target als String und .__ von Ihnen benötigt wird, können Sie die Konvertierung direkt mit target string durchführen

extension NSRange {
    public init(_ range:Range<String.Index>) {
        self.init(location: range.lowerBound.encodedOffset,
              length: range.upperBound.encodedOffset -
                      range.lowerBound.encodedOffset) }
    }

oder Sie können eine spezielle Erweiterung für Range selbst erstellen

extension Range where Bound == String.Index {
    var nsRange:NSRange {
    return NSRange(location: self.lowerBound.encodedOffset,
                     length: self.upperBound.encodedOffset -
                             self.lowerBound.encodedOffset)
    }
}

Verwendungszweck:

let string = "Many animals here: ???????????? !!!"
if let range = string.range(of: "????????????"){
    print((string as NSString).substring(with: NSRange(range))) //  "????????????"
}

oder

if let nsrange = string.range(of: "????????????")?.nsRange{
    print((string as NSString).substring(with: nsrange)) //  "????????????"
}
5
Dmitry A.

Für mich funktioniert das perfekt:

let font = UIFont.systemFont(ofSize: 12, weight: .medium)
let text = "text"
let attString = NSMutableAttributedString(string: "exemple text :)")

attString.addAttributes([.font: font], range:(attString.string as NSString).range(of: text))

label.attributedText = attString
4
Breno Vinícios

Swift 3 Extension Variant , die vorhandene Attribute beibehält. 

extension UILabel {
  func setLineHeight(lineHeight: CGFloat) {
    guard self.text != nil && self.attributedText != nil else { return }
    var attributedString = NSMutableAttributedString()

    if let attributedText = self.attributedText {
      attributedString = NSMutableAttributedString(attributedString: attributedText)
    } else if let text = self.text {
      attributedString = NSMutableAttributedString(string: text)
    }

    let style = NSMutableParagraphStyle()
    style.lineSpacing = lineHeight
    style.alignment = self.textAlignment
    let str = NSString(string: attributedString.string)

    attributedString.addAttribute(NSParagraphStyleAttributeName,
                                  value: style,
                                  range: str.range(of: str as String))
    self.attributedText = attributedString
  }
}
1
jriskin

Ich liebe die Swift-Sprache, aber die Verwendung von NSAttributedString mit einer Swift Range, die nicht mit NSRange kompatibel ist, hat meinen Kopf zu lange verletzt. Um den ganzen Müll zu umgehen, habe ich die folgenden Methoden entwickelt, um eine NSMutableAttributedString mit den hervorgehobenen Wörtern in Ihrer Farbe zurückzugeben. 

Dies funktioniert nicht für Emojis. Ändern Sie, wenn Sie müssen.

extension String {
    func getRanges(of string: String) -> [NSRange] {
        var ranges:[NSRange] = []
        if contains(string) {
            let words = self.components(separatedBy: " ")
            var position:Int = 0
            for Word in words {
                if Word.lowercased() == string.lowercased() {
                    let startIndex = position
                    let endIndex = Word.characters.count
                    let range = NSMakeRange(startIndex, endIndex)
                    ranges.append(range)
                }
                position += (Word.characters.count + 1) // +1 for space
            }
        }
        return ranges
    }
    func highlight(_ words: [String], this color: UIColor) -> NSMutableAttributedString {
        let attributedString = NSMutableAttributedString(string: self)
        for Word in words {
            let ranges = getRanges(of: Word)
            for range in ranges {
                attributedString.addAttributes([NSForegroundColorAttributeName: color], range: range)
            }
        }
        return attributedString
    }
}

Verwendungszweck:

// The strings you're interested in
let string = "The dog ran after the cat"
let words = ["the", "ran"]

// Highlight words and get back attributed string
let attributedString = string.highlight(words, this: .yellow)

// Set attributed string
label.attributedText = attributedString
0
Brandon A

Schnell 4

Ich denke, es gibt zwei Möglichkeiten.

1. NSRange (Bereich, in:)

2. NSRange (location :, length:)

Beispielcode:

let attributedString = NSMutableAttributedString(string: "Sample Text 12345", attributes: [.font : UIFont.systemFont(ofSize: 15.0)])

// NSRange(range, in: )
if let range = attributedString.string.range(of: "Sample")  {
    attributedString.addAttribute(.foregroundColor, value: UIColor.orange, range: NSRange(range, in: attributedString.string))
}

// NSRange(location: , length: )
if let range = attributedString.string.range(of: "12345") {
    attributedString.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: range.lowerBound.encodedOffset, length: range.upperBound.encodedOffset - range.lowerBound.encodedOffset))
}

Bildschirmfoto:  enter image description here

0
Den
func formatAttributedStringWithHighlights(text: String, highlightedSubString: String?, formattingAttributes: [String: AnyObject]) -> NSAttributedString {
    let mutableString = NSMutableAttributedString(string: text)

    let text = text as NSString         // convert to NSString be we need NSRange
    if let highlightedSubString = highlightedSubString {
        let highlightedSubStringRange = text.rangeOfString(highlightedSubString) // find first occurence
        if highlightedSubStringRange.length > 0 {       // check for not found
            mutableString.setAttributes(formattingAttributes, range: highlightedSubStringRange)
        }
    }

    return mutableString
}
0
orkoden