Кернинг в iOS UITextView
Для того, что, по-видимому, является "ошибкой", UITextView, похоже, не поддерживает кернинг, как другие элементы UIKit. Есть ли способ обхода работы кернинга?
Чтобы быть ясным, я говорю о промежутке между парами символов, которые определены в файле шрифта, а не общим интервалом между всеми символами. Использование NSAttributedString
с NSKernAttributeName
будет регулировать расстояние между всеми символами, но встроенные пары символов все равно не работают.
Пример:
![enter image description here]()
Есть ли способ обхода проблемы?
Один простой обходной путь, который я обнаружил, - добавить стили css в UITextView, которые позволяют кернинг. Если я использую недокументированный метод setContentToHTMLString:
, я могу ввести css в секретный веб-просмотр в текстовом представлении.
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 в текстовое представление?
Другие методы, с которыми я экспериментировал, включают:
Использование веб-представления и контент-доступного свойства. Это не идеально, потому что веб-просмотр делает кучу лишних вещей, которые мешают, например, вид вспомогательного аксессуара, который нельзя скрыть.
Подклассификация a UITextView
и рендеринг текста вручную с помощью основного текста. Это сложнее, чем я надеялся, потому что все элементы выбора тоже нужно переконфигурировать. Из-за UITextView
частных подклассов UITextPosition
и UITextRange
это огромная боль в попке, если не совсем невозможно.
Ответы
Ответ 1
Вы правы, что ваше приложение, вероятно, будет отклонено с помощью @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.
Ответ 2
Попробуйте следующее:
[textView_ setValue:@"hello, <b>world</b>" forKey:@"contentToHTMLString"];
Он работает в моем тесте. Конечно, я не пробовал его в магазине приложений.
Ответ 3
После небольшого исследования я обнаружил, что причиной отсутствия Кернинга является то, что 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.
Ответ 4
Вы посмотрели здесь: https://github.com/AliSoftware/OHAttributedLabel? Если посмотреть на этот проект, похоже, что в магазине приложений есть несколько приложений, которые используют разметку (предоставленную этим классом). Возможно, вы можете использовать этот класс или получить от него некоторые идеи реализации. Возможно, ваше приложение as-is пройдет.
Ответ 5
С iOS6, UITextView
имеет новый атрибут attributedText
, которому вы можете назначить NSAttributedString
. Я не пробовал, но было бы интересно посмотреть, получится ли вам кернинг, если вы установите этот атрибут вашего текстового вида, а не атрибут text
. И это добавляет преимущество публичного интерфейса.
Ответ 6
Не нужно прибегать к частным методам. Вы можете легко подделать его, динамически изменяя свойство 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];
}