2015-05-22 3 views
6

Ich habe die folgende Funktion zu finden und markieren Sie Hashtags oder erwähnt (@ oder #) in einer UILabel:Wie markiere ich Text in einer Zeichenfolge, die Emojis in Swift enthält?

class func addLinkAttribute(pattern: String, 
     toText text: String, 
     withAttributeName attributeName : String, 
     toAttributedString attributedString :NSMutableAttributedString, 
     withLinkAttributes linkAttributes: [NSObject : AnyObject]) { 
     var error: NSError? 
     if let regex = NSRegularExpression(pattern: pattern, options:.CaseInsensitive, error: &error) { 
      regex.enumerateMatchesInString(text, options: .allZeros, range: NSMakeRange(0, count(text))) { result, flags, stop in 
       let range = result.range 
       let start = advance(text.startIndex, range.location) 
       let end = advance(start, range.length) 
       let foundText = text.substringWithRange(Range<String.Index>(start: start,end: end)) 
       var linkAttributesWithName = linkAttributes 
       linkAttributesWithName[attributeName] = foundText 
       attributedString.addAttributes(linkAttributesWithName, range: range) 
      } 
     } 
    } 

Wenn ich einen Hashtag (#)(\\w+) oder erwähnen (@)(\\w+) Muster übergeben Sie den Code perfekt funktioniert, aber wenn der Text ein enthält emoji der Bereich durch die Anzahl der Emojis versetzt vorhergehenden es:

enter image description here

I Swift behandelt Strings anders zu Objective-C kennen, da count(string) und count(string.utf16) geben mir andere Ergebnisse, aber ich bin ratlos, wie dies bei Verwendung eines regulären Ausdrucks zu berücksichtigen ist.

Ich könnte nur den Unterschied zwischen den 2 Zählern überprüfen und den Bereich ausgleichen, aber das scheint falsch und hacky zu mir. Es muss einen anderen Weg geben.

Antwort

8

Ähnlich wie in Swift extract regex matches, ist eine mögliche Lösung ist, die gegeben Swift String auf ein NSString zu konvertieren und wenden die NSRange S durch enumerateMatchesInString() zu dieser NSString zurückgegeben:

class func addLinkAttribute(pattern: String, 
    toText text: String, 
    withAttributeName attributeName : String, 
    toAttributedString attributedString :NSMutableAttributedString, 
    withLinkAttributes linkAttributes: [NSObject : AnyObject]) { 
    let nsText = text as NSString 
    var error: NSError? 
    if let regex = NSRegularExpression(pattern: pattern, options:.CaseInsensitive, error: &error) { 
     regex.enumerateMatchesInString(text, options: .allZeros, range: NSMakeRange(0, nsText.length)) { 
      result, _, _ in 
      let range = result.range 
      let foundText = nsText.substringWithRange(range) 
      var linkAttributesWithName = linkAttributes 
      linkAttributesWithName[attributeName] = foundText 
      attributedString.addAttributes(linkAttributesWithName, range: range) 
     } 
    } 
} 

(Alternativlösung .) Es ist möglich, NSRange in Range<String.Index> ohne Zwischenkonvertierung inumzuwandeln. Mit

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

von https://stackoverflow.com/a/30404532/1187415, kann Ihr Code als

geschrieben werden
class func addLinkAttribute(pattern: String, 
    toText text: String, 
    withAttributeName attributeName : String, 
    toAttributedString attributedString :NSMutableAttributedString, 
    withLinkAttributes linkAttributes: [NSObject : AnyObject]) { 

    var error: NSError? 
    if let regex = NSRegularExpression(pattern: pattern, options:.CaseInsensitive, error: &error) { 
     regex.enumerateMatchesInString(text, options: .allZeros, range: NSMakeRange(0, count(text.utf16))) { 
      result, _, _ in 
      let nsRange = result.range 
      if let strRange = text.rangeFromNSRange(nsRange) { 
       let foundText = text.substringWithRange(strRange) 
       var linkAttributesWithName = linkAttributes 
       linkAttributesWithName[attributeName] = foundText 
       attributedString.addAttributes(linkAttributesWithName, range: nsRange) 
      } 
     } 
    } 
} 

und das sollte auch richtig für alle Arten von erweiterten grapheme Clustern (Emojis, Regionale Indikatoren etc ...) arbeiten

+0

Funktioniert perfekt! Vielen Dank. Ich bin mir sicher, dass es einen Weg gibt, dies zu tun, ohne sich auf 'NSString' zu verlassen, aber das funktioniert und ich glaube nicht, dass' NSString' bald irgendwo hin geht. –

+1

@MichaelGaylord: Du hast meinen Ehrgeiz erregt, siehe aktualisierte Antwort :) –

+0

@MartinR genial! Danke für das Teilen! funktioniert wie ein Charme! – Andres

Verwandte Themen