Kerning w iOS UITextView

Dla tego, co najwyraźniej jest 'błędem', UITextView nie wydaje się wspierać kerningu, jak inne elementy UIKit. Czy istnieje obejście, aby kerning działał?

Żeby było jasne, mówię o odstępach między parami znaków, które są zdefiniowane w pliku czcionki, a nie o ogólnym odstępie między wszystkimi znakami. Użycie {[1] } z NSKernAttributeName dostosuje odstępy między wszystkimi znakami, ale wbudowane pary znaków nadal nie działają.

Dla Przykład:

Tutaj wpisz opis obrazka

Czy istnieje obejście tego problemu?

Jednym z prostych rozwiązań, które odkryłem, jest dodanie stylów css do UITextView, które umożliwiają kerning. Jeśli użyję nieudokumentowanej metody setContentToHTMLString:, mogę wprowadzić css do tajnego widoku sieci Web w widoku tekstowym.

NSString *css = @"*{text-rendering: optimizeLegibility;}";
NSString *html = [NSString stringWithFormat:@"<html><head><style>%@</style></head><body>Your HTML Text</body></html>", css];
[textView performSelector:@selector(setContentToHTMLString:) withObject:html];

To rozwiązuje problem natychmiast; jednak wydaje się bardzo prawdopodobne, że spowoduje to odrzucenie aplikacji. Czy istnieje bezpieczny sposób, aby przemycić css do widoku tekstowego?

Inne obejścia, z którymi eksperymentowałem, obejmują:

Używanie widoku sieci Web i właściwości contenteditable. Nie jest to idealne rozwiązanie, ponieważ webview robi kilka dodatkowych rzeczy, które wchodzą w drogę, na przykład widok akcesoriów wejściowych, które nie mogą być łatwo ukryte.

Podklasowanie a UITextView i ręczne renderowanie tekstu z tekstem głównym. Jest to bardziej złożone, niż się spodziewałem, ponieważ wszystkie elementy selekcji również wymagają rekonfiguracji. Ze względu na prywatne podklasy UITextView z UITextPosition i UITextRange to to ogromny wrzód na dupie, jeśli nie całkowicie niemożliwe.

Author: Anthony Mattox, 2012-11-05

6 answers

Masz rację, że Twoja aplikacja prawdopodobnie zostanie odrzucona przez użycie @selector(setContentToHTMLString:). Można jednak użyć prostej sztuczki, aby dynamicznie skonstruować selektor, aby nie został wykryty podczas walidacji.

NSString *css = @"*{text-rendering: optimizeLegibility;}";
NSString *html = [NSString stringWithFormat:@"<html><head><style>%@</style></head><body>Your HTML Text</body></html>", css];
@try {
    SEL setContentToHTMLString = NSSelectorFromString([@[@"set", @"Content", @"To", @"HTML", @"String", @":"] componentsJoinedByString:@""]);
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [textView performSelector:setContentToHTMLString withObject:html];
    #pragma clang diagnostic pop
}
@catch (NSException *exception) {
    // html+css could not be applied to text view, so no kerning
}

Owijając wywołanie wewnątrz bloku @ try/ @ catch, możesz również upewnić się, że aplikacja nie ulegnie awarii, jeśli Metoda setContentToHTMLString: zostanie usunięta w przyszłej wersji iOS.

Korzystanie z prywatnych interfejsów API nie jest ogólnie zalecane, ale w tym przypadku myślę, że całkowicie sensowne jest użycie małego hack vs przepisywanie UITextView z CoreText.

 36
Author: 0xced,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-14 21:43:31

Spróbuj tego:

[textView_ setValue:@"hello, <b>world</b>" forKey:@"contentToHTMLString"];
To działa w moim teście. Oczywiście nie próbowałem tego w app store.
 1
Author: rob mayoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-15 06:36:11

Po zbadaniu tego trochę odkryłem, że powodem braku kerningu jest to, że UITextView wewnętrznie używa UIWebDocumentView, który ma Kerning wyłączony domyślnie.

Kilka informacji o wewnętrznym działaniu UITextView: http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/

Niestety nie ma usankcjonowanej metody umożliwiającej Kerningowanie, zdecydowanie odradzałbym użycie hacka przy użyciu zakamuflowanego selektora.

Oto mój Radar, proponuję oszukać: http://www.cocoanetics.com/2012/12/radar-uitextview-ignores-font-kerning/

W tym twierdzę, że podczas ustawiania tekstu na UITextView przez setAttributedText, Programiści oczekują, że Kerning będzie domyślnie włączony, ponieważ w ten sposób będzie najbardziej dopasowany do wyniku renderowania tekstu przez CoreText.

 1
Author: Cocoanetics,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-16 18:54:46

Zajrzałeś tutaj: https://github.com/AliSoftware/OHAttributedLabel ? patrząc na ten projekt, wydaje się, że w app store jest kilka aplikacji, które używają znaczników (dostarczonych przez tę klasę). Być może mógłbyś użyć tej klasy lub zebrać z niej kilka pomysłów na implementację. Być może Twoja aplikacja w stanie, w jakim jest, przejdzie zbiórkę.

 0
Author: Mark DiGiovanni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-09 15:48:06

Z iOS6, UITextView posiada nowy atrybut attributedText, do którego można przypisać NSAttributedString. Nie próbowałem tego, ale byłoby interesujące zobaczyć, czy masz kerning, którego szukasz, jeśli ustawisz ten atrybut widoku tekstu, a nie atrybut text. I ma dodatkową zaletę bycia interfejsem publicznym.

 0
Author: Mark Granoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-14 21:51:30

Nie ma potrzeby uciekania się do prywatnych metod. Możesz go łatwo sfałszować, dynamicznie zmieniając Właściwość attributedText pola tekstowego za każdym razem, gdy tekst się zmieni, subskrybując powiadomienie UITextFieldTextDidChangeNotification:

- (void)viewDidLoad {
    // your regular stuff goes here
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChangeNotification:) name:UITextFieldTextDidChangeNotification object:nil];
}


- (void)textDidChangeNotification:(NSNotification *)notification {
    // you can of course specify other attributes here, such as font and text color
    CGFloat kerning = -3.; // change accordingly to your desired value
    NSDictionary *attributes = @{
                                 NSKernAttributeName: @(kerning),
                                 };
    UITextField *textField = [notification object];
    textField.attributedText = [[NSAttributedString alloc] initWithString:textField.text attributes:attributes];
}
 -1
Author: boliva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-14 15:42:43