Problem: NSAttributedString pobiera NSRange, gdy używam Swift String, który używa 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)
}
})
Powoduje następujący błąd:
błąd: „Zakres” nie jest konwertowany na „NSRange” attributeString.addAttribute (NSForegroundColorAttributeName, wartość: NSColor.redColor (), zakres: substringRange)
Odpowiedzi:
Szybkie
String
zakresy iNSString
zakresy nie są „zgodne”. Na przykład emoji, takie jak 😄, liczy się jako jeden znak Swift, ale jako dwaNSString
znaki (tak zwana para zastępcza UTF-16).Dlatego sugerowane rozwiązanie przyniesie nieoczekiwane wyniki, jeśli ciąg zawiera takie znaki. Przykład:
Wynik:
Jak widać, „ph say” zostało oznaczone atrybutem, a nie „mówiąc”.
Ponieważ
NS(Mutable)AttributedString
ostatecznie wymaga anNSString
iNSRange
, w rzeczywistości lepiej jest przekonwertować dany ciąg naNSString
pierwszy. WtedysubstringRange
jest anNSRange
i nie musisz już konwertować zakresów:Wynik:
Aktualizacja dla Swift 2:
Aktualizacja dla Swift 3:
Aktualizacja dla Swift 4:
Począwszy od Swift 4 (Xcode 9), standardowa biblioteka Swift zapewnia metodę konwersji między
Range<String.Index>
iNSRange
. Konwersja naNSString
nie jest już konieczna:Tutaj
substringRange
jestRange<String.Index>
, i to jest konwertowane na odpowiadająceNSRange
zźródło
Range<String.Index>
iNSString
nie jesteś zgodny. Czy ich odpowiedniki również są niekompatybilne? To znaczy sąNSRange
iString
niekompatybilne? Ponieważ jeden z API firmy Apple łączy w szczególności dwa: dopasowania (w: opcje: zakres :)W przypadkach takich jak ten, który opisałeś, okazało się, że działa. Jest stosunkowo krótki i słodki:
źródło
NSRange
.str
jestNSString
i dlategostr.RangeOfString()
zwracaNSRange
.let str = attributedString.string as NSString
Odpowiedzi są w porządku, ale dzięki Swift 4 możesz nieco uprościć kod:
Bądź ostrożny, ponieważ wynik
range
funkcji musi zostać rozpakowany.źródło
Możliwe rozwiązanie
Swift zapewnia distance (), która mierzy odległość między początkiem a końcem, której można użyć do utworzenia NSRange:
źródło
U mnie to działa idealnie:
źródło
Swift 4:
Jasne, wiem, że Swift 4 ma już rozszerzenie dla NSRange
Wiem, że w większości przypadków ten init jest wystarczający. Zobacz jego zastosowanie:
Ale konwersję można wykonać bezpośrednio z Range <String.Index> na NSRange bez wystąpienia String języka Swift.
Zamiast ogólnego użycia init, które wymaga od ciebie parametru docelowego jako String, a jeśli nie masz pod ręką docelowego ciągu, możesz bezpośrednio utworzyć konwersję
lub możesz utworzyć specjalistyczne rozszerzenie dla samego zakresu
Stosowanie:
lub
Swift 5:
Ze względu na migrację ciągów Swift do kodowania UTF-8 domyślnie, użycie
encodedOffset
jest uważane za przestarzałe, a Range nie można przekonwertować na NSRange bez wystąpienia samego ciągu, ponieważ do obliczenia przesunięcia potrzebujemy ciągu źródłowego, który jest zakodowany w UTF-8 i powinien zostać przekonwertowany na UTF-16 przed obliczeniem przesunięcia. Na razie najlepszym rozwiązaniem jest więc użycie generycznego init .źródło
encodedOffset
jest uważane za szkodliwe i zostanie wycofane .Szybki 4
Myślę, że są dwa sposoby.
1. NSRange (zakres, w:)
2. NSRange (lokalizacja :, długość:)
Przykładowy kod:
Zrzut ekranu:
źródło
encodedOffset
jest uważane za szkodliwe i zostanie wycofane .Wariant rozszerzenia Swift 3, który zachowuje istniejące atrybuty.
źródło
źródło
Uwielbiam język Swift, ale używanie go
NSAttributedString
z językiem Swift,Range
który nie jest kompatybilnyNSRange
, sprawiało, że bolała mnie głowa zbyt długo. Aby ominąć te wszystkie śmieci, opracowałem następujące metody zwracania znakuNSMutableAttributedString
z wyróżnionymi słowami ustawionymi w Twoim kolorze.To nie działa w przypadku emoji. Zmodyfikuj, jeśli musisz.
Stosowanie:
źródło
źródło