在iOS UITextView中进行Kerning

时间:2012-11-05 19:48:41

标签: objective-c ios css uitextview kerning

对于显然是'bug',UITextView似乎不像其他UIKit Elements那样支持字距调整。是否有解决方法来使字距调整工作?

要清楚,我在谈论字体文件中定义的字符对之间的间距,而不是所有字符之间的一般间距。使用NSAttributedStringNSKernAttributeName将调整所有字符之间的间距,但内置字符对仍无效。

例如:

enter image description here

有解决方法吗?

我发现的一个简单的解决方法是向UITextView添加css样式以启用字距调整。如果我使用未记录的方法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会阻碍一些额外的东西,例如输入附件视图,这些视图无法轻易隐藏。

UITextView进行子类化并使用核心文本手动渲染文本。这比我希望的更复杂,因为所有选择的东西也需要重新配置。由于UITextView的私有子类UITextPositionUITextRange,如果不是完全不可能的话,这是一个巨大的痛苦。

6 个答案:

答案 0 :(得分:36)

您是正确的,使用@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块中,您还可以确保在将来的iOS版本中删除setContentToHTMLString:方法时,您的应用不会崩溃。

通常不推荐使用私有API,但在这种情况下,我认为使用小型hack与使用CoreText重写UITextView完全有意义。

答案 1 :(得分:2)

在研究了一下之后,我发现缺少Kerning的原因是UITextView内部使用的UIWebDocumentView默认情况下关闭了Kerning。

有关UITextView内部工作原理的一些信息:http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/

不幸的是,没有制裁方法来启用Kerning,我肯定会建议不要使用伪装选择器进行黑客攻击。

这是我的雷达,我建议你欺骗它:http://www.cocoanetics.com/2012/12/radar-uitextview-ignores-font-kerning/

在此我认为,当通过setAttributedText在UITextView上设置文本时,我们开发人员希望默认情况下启用Kerning,因为它最接近匹配通过CoreText呈现文本的输出。

答案 2 :(得分:1)

试试这个:

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

它适用于我的测试。我当然没有在应用程序商店中尝试过它。

答案 3 :(得分:0)

你看过这里:https://github.com/AliSoftware/OHAttributedLabel?看看这个项目,app store中有几个使用标记的应用程序(由此类提供)。也许您可以使用此类或从中收集一些实现想法。也许你的应用程序将会通过审核。

答案 4 :(得分:0)

对于iOS6,UITextView有一个新属性attributedText,您可以为其分配NSAttributedString。我没有尝试过,但是如果你设置了文本视图的这个属性而不是text属性,那么看看你是否得到了你想要的字距就会很有趣。它还具有成为公共界面的额外好处。

答案 5 :(得分:-1)

没有必要采用私人方法。您可以通过订阅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];
}