2015-05-22 3 views
6

Ho la seguente funzione per trovare ed evidenziare hashtag o menzioni (@ o #) in un UILabel:Come si evidenzia il testo in una stringa che contiene emoji in Swift?

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

Se mi passa un hashtag (#)(\\w+) o citare (@)(\\w+) modello il codice funziona perfettamente, ma se il testo contiene un emoji la gamma è compensato dal numero di emoji che lo precede:

enter image description here

so Tratta Swift stringhe in modo diverso a Objective-C, dal momento che count(string) e count(string.utf16) mi danno risultati diversi, ma sono perplesso su come tener conto di ciò quando si usa un'espressione regolare.

Potrei semplicemente controllare la differenza tra i 2 conteggi e compensare l'intervallo, ma questo mi sembra sbagliato e intrusivo. Deve esserci un altro modo.

risposta

8

Allo stesso modo come in Swift extract regex matches, una possibile soluzione è quella di convertire le Swift String a un NSString e applicare la NSRange s restituito da enumerateMatchesInString() a quella NSString:

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

(soluzione alternativa .) È possibile convertire un NSRange in Range<String.Index> senza conversione intermedia in un NSString. Con

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

dal https://stackoverflow.com/a/30404532/1187415, il codice può essere scritto come

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

e che dovrebbe anche funzionare correttamente per tutti i tipi di estese grafema cluster (emoji, indicatori regionali, ecc ...)

+0

Funziona perfettamente! Grazie. Sono sicuro che c'è un modo per farlo senza fare affidamento su 'NSString', ma questo funziona e non credo che' NSString' stia andando da qualche parte presto. –

+1

@ MichaelGaylord: hai suscitato la mia ambizione, vedi risposta aggiornata :) –

+0

@ Martin fantastico! Grazie per la condivisione! funziona come un fascino! – Andres