2015-05-22 5 views
6

Mam następującą funkcję, aby znaleźć i podświetlić hashtags lub wymienia (@ lub #), w UILabel:Jak wyróżnić tekst w ciągu zawierającym emotikony w 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) 
      } 
     } 
    } 

Jeśli mijam hashtag (#)(\\w+) lub wymienić (@)(\\w+) wzór kod działa doskonale, ale jeśli tekst zawiera Emotikon zakres jest równoważone przez liczbę emotikony poprzedzający go:

enter image description here

wiem Swift leczy ciągi inaczej Objective-C, ponieważ count(string) i count(string.utf16) daje mi inne wyniki, ale nie jestem pewien, jak to wyjaśnić, używając wyrażenia regularnego.

Mogę po prostu sprawdzić różnicę między 2 liczbami i zrównoważyć zakres, ale wydaje mi się to nieprzyzwoite i hackowate. Musi być inny sposób.

Odpowiedz

8

Podobnie jak w Swift extract regex matches, możliwym rozwiązaniem jest konwersja danych Swift String do NSString i stosuje NSRange s 'o enumerateMatchesInString() tej 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) 
     } 
    } 
} 

(Alternatywne rozwiązanie .) Możliwe jest przekonwertowanie NSRange na Range<String.Index> bez pośredniej konwersji na NSString. Z

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

od https://stackoverflow.com/a/30404532/1187415, kod może być napisany jako

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

i które powinny również działać poprawnie dla wszystkich rodzajów udzielonych grafem klastrów (emotikony, regionalnych wskaźników, etc ...)

+0

Działa idealnie! Dziękuję Ci. Jestem pewien, że jest sposób na zrobienie tego bez polegania na 'NSString' ale to działa i nie sądzę, że' NSString' wkrótce się pojawi. –

+1

@MichaelGaylord: Pobudziłeś moją ambicję, zobacz zaktualizowaną odpowiedź :) –

+0

@MartinR awesome! Dzięki za udostępnienie! działa jak urok! – Andres