Problema: NSAttributedString usa um NSRange enquanto estou usando uma Swift String que usa Range
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)
}
})
Produz o seguinte erro:
erro: 'Range' não é convertível em 'NSRange' attributeString.addAttribute (NSForegroundColorAttributeName, valor: NSColor.redColor (), range: substringRange)
Respostas:
Os
String
intervalos e osNSString
intervalos rápidos não são "compatíveis". Por exemplo, um emoji como 😄 conta como um caractere Swift, mas como doisNSString
caracteres (o chamado par substituto UTF-16).Portanto, sua solução sugerida produzirá resultados inesperados se a sequência contiver esses caracteres. Exemplo:
Resultado:
Como você vê, "ph say" foi marcado com o atributo, não "dizendo".
Como, em
NS(Mutable)AttributedString
última análise, requer umNSString
e umNSRange
, é realmente melhor converter a sequência fornecida emNSString
primeiro. Então osubstringRange
é umNSRange
e você não precisa mais converter os intervalos:Resultado:
Atualização para o Swift 2:
Atualização para o Swift 3:
Atualização para o Swift 4:
A partir do Swift 4 (Xcode 9), a biblioteca padrão do Swift fornece um método para converter entre
Range<String.Index>
eNSRange
. A conversão paraNSString
não é mais necessária:Aqui
substringRange
está umRange<String.Index>
, e que é convertido no correspondenteNSRange
comfonte
Range<String.Index>
eNSString
não é compatível. Os colegas também são incompatíveis? Ou seja, sãoNSRange
eString
incompatíveis? Porque uma das APIs da Apple combina especificamente os dois: fósforos (em: opções: intervalo :)Para casos como o que você descreveu, achei que funcionava. É relativamente curto e agradável:
fonte
NSRange
.str
é umNSString
e, portanto,str.RangeOfString()
retorna umNSRange
.let str = attributedString.string as NSString
As respostas são boas, mas com o Swift 4 você pode simplificar um pouco o seu código:
Seja cauteloso, pois o resultado da
range
função deve ser desembrulhado.fonte
Solução possível
Swift fornece distance (), que mede a distância entre o início e o final que pode ser usada para criar um NSRange:
fonte
Para mim, isso funciona perfeitamente:
fonte
Swift 4:
Claro, eu sei que o Swift 4 já tem uma extensão para o NSRange
Eu sei que na maioria dos casos esse init é suficiente. Veja seu uso:
Mas a conversão pode ser feita diretamente de Range <String.Index> para NSRange sem a instância String de Swift.
Em vez do uso genérico do init , que exige de você o parâmetro target como String e, se você não tem a string alvo em mãos, pode criar a conversão diretamente
ou você pode criar a extensão especializada para o próprio Range
Uso:
ou
Swift 5:
Devido à migração das strings Swift para a codificação UTF-8 por padrão, o uso de
encodedOffset
é considerado obsoleto e o Range não pode ser convertido em NSRange sem uma instância da própria String, porque, para calcular o deslocamento, precisamos da string de origem que é codificado em UTF-8 e deve ser convertido em UTF-16 antes de calcular o deslocamento. Portanto, a melhor abordagem, por enquanto, é usar init genérico .fonte
encodedOffset
é considerado prejudicial e será preterido .Swift 4
Eu acho que existem duas maneiras.
1. NSRange (intervalo, em:)
2. NSRange (localização :, comprimento:)
Código de amostra:
Captura de tela:
fonte
encodedOffset
é considerado prejudicial e será preterido .Variante de extensão Swift 3 que preserva os atributos existentes.
fonte
fonte
Eu amo a linguagem Swift, mas usar
NSAttributedString
uma SwiftRange
que não é compatívelNSRange
fez minha cabeça doer por muito tempo. Então, para contornar todo esse lixo, criei os seguintes métodos para retornar umNSMutableAttributedString
com as palavras destacadas definidas com a sua cor.Isso não funciona para emojis. Modifique se necessário.
Uso:
fonte
fonte