Кернинг в iOS UITextView

для того, что, по-видимому, является "ошибкой", UITextView, похоже, не поддерживает Кернинг, как и другие элементы UIKit. Есть ли обходной путь, чтобы заставить kerning работать?

чтобы быть ясным, я говорю о интервале между парами символов, которые определены в файле шрифта, а не об общем интервале между всеми символами. С помощью NSAttributedString С NSKernAttributeName отрегулирует расстояние между всеми символами, но встроенные пары символов по-прежнему не работают.

для Пример:

enter image description here

есть ли обходной путь, чтобы исправить это?

один простой обходной путь, который я обнаружил, - это добавить стили css в UITextView, которые включают Кернинг. Если я использую недокументированный метод setContentToHTMLString: Я могу ввести css в секретный webview в текстовом представлении.

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];

это немедленно устраняет проблему; тем не менее, кажется очень вероятным, что это приведет к отклонению приложения. Есть ли безопасный способ проникнуть в css текстовое представление?

другие обходные пути, с которыми я экспериментировал, включают:

использование webview и свойства contenteditable. Это не идеально, потому что webview делает кучу дополнительных вещей, которые мешают, например, вид входного аксессуара, который нелегко скрыть.

подкласс a UITextView и рендеринга текста вручную с основным текстом. Это сложнее, чем я надеялся, потому что все материалы выбора также должны быть перенастроены. Потому что UITextViewчастные подклассы UITextPosition и UITextRange это огромная боль в заднице, если не совершенно невозможно.

6 ответов


вы правы, что ваше приложение, вероятно, будет отклонено с помощью @selector(setContentToHTMLString:). Однако вы можете использовать простой трюк для динамического построения селектора, чтобы он не был обнаружен во время проверки.

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
}

обернув вызов внутри блока @try/@catch, вы также гарантируете, что ваше приложение не рухнет, если setContentToHTMLString: метод удаляется в будущей версии iOS.

использование частных API обычно не рекомендуется, но в этом случае я думаю, что это полностью делает смысл использовать небольшой хак против перезаписи UITextView с CoreText.


попробуйте это:

[textView_ setValue:@"hello, <b>world</b>" forKey:@"contentToHTMLString"];

он работает в моем тесте. Конечно, я не пробовал это в app store.


после исследования этого немного я обнаружил, что причина отсутствия Кернинга заключается в том, что UITextView внутренне использует UIWebDocumentView, у которого Кернинг выключен по умолчанию.

некоторые сведения о внутренней работе UITextView:http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/

к сожалению, нет санкционированного метода для включения Кернинга, я бы определенно не советовал использовать Хак, используя замаскированный селектор.

вот мой радар, я предлагаю вам обмануть его:http://www.cocoanetics.com/2012/12/radar-uitextview-ignores-font-kerning/

в этом я утверждаю, что при настройке текста на UITextView через setAttributedText мы, разработчики, ожидаем, что Кернинг будет включен по умолчанию, потому что именно так он будет наиболее точно соответствовать выходу рендеринга текста через CoreText.


вы взглянули сюда:https://github.com/AliSoftware/OHAttributedLabel? Глядя на этот проект, кажется, что есть несколько приложений в app store которые используют разметку (предоставленную этим классом). Возможно, вы могли бы использовать этот класс или извлечь из него некоторые идеи реализации. Возможно, ваше приложение as-is пройдет сбор.


С iOS6, UITextView присвоен новый атрибут attributedText, которому можно назначить NSAttributedString. Я не пробовал, но было бы интересно посмотреть, получите ли вы Кернинг, который вам нужен, если вы установите этот атрибут своего текстового представления, а не . И это имеет дополнительное преимущество быть публичным интерфейсом.


нет необходимости прибегать к частным методам. Вы можете легко подделать его, динамически изменяя attributedText свойство текстового поля при каждом изменении текста путем подписки на 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];
}