Crénage dans iOS UITextView
Pour ce qui est apparemment un 'bug', UITextView ne semble pas supporter le crénage comme les autres éléments UIKit. Existe-t-il une solution de contournement pour faire fonctionner le crénage?
Pour être clair, je parle de l'espacement entre les paires de caractères définies dans le fichier de police, pas de l'espacement général entre tous les caractères. L'utilisation d'un NSAttributedString
avec le {[2] } ajustera l'espacement entre tous les caractères, mais les paires de caractères intégrées ne fonctionnent toujours pas.
Pour Exemple:
Existe-t-il une solution de contournement pour résoudre ce problème?
Une solution de contournement simple que j'ai découverte est d'ajouter des styles css à L'UITextView qui permettent le crénage. Si j'utilise la méthode non documentée setContentToHTMLString:
, je peux injecter le css à la webview secrète dans la vue texte.
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];
Cela résout le problème immédiatement; cependant, il semble très probable que l'application soit rejetée. Existe-t-il un moyen sûr de faufiler du css dans la vue texte?
Autres les solutions de contournement que j'ai expérimentées incluent:
Utilisation d'un webview et de la propriété contenteditable. Ce n'est pas idéal car webview fait un tas de choses supplémentaires qui gênent, par exemple la vue des accessoires d'entrée qui ne peut pas facilement être cachée.
Sous-classer un UITextView
et rendre le texte manuellement avec le texte de base. C'est plus complexe que je ne l'espérais, car toutes les sélections doivent également être reconfigurées. En raison des sous-classes privées de UITextView
de UITextPosition
et UITextRange
ceci est une énorme douleur dans le cul sinon complètement impossible.
6 réponses
Vous avez raison que votre application serait probablement rejetée en utilisant @selector(setContentToHTMLString:)
. Vous pouvez cependant utiliser une astuce simple pour construire le sélecteur dynamiquement afin qu'il ne soit pas détecté lors de la validation.
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
}
En enveloppant l'appel dans un bloc @ try/ @ catch, vous vous assurez également que votre application ne se bloque pas si la méthode setContentToHTMLString:
est supprimée dans une future version d'iOS.
L'utilisation D'API privées n'est généralement pas recommandée, mais dans ce cas, je pense qu'il est tout à fait logique d'utiliser un petit hack vs réécriture UITextView avec CoreText.
Essayez ceci:
[textView_ setValue:@"hello, <b>world</b>" forKey:@"contentToHTMLString"];
Cela fonctionne dans mon test. Je ne l'ai pas essayé dans l'App store bien sûr.
Après avoir fait des recherches un peu, j'ai trouvé que la raison du crénage manquant est que UITextView utilise en interne un Uiwebdocumentview dont le crénage est désactivé par défaut.
Quelques infos sur le fonctionnement interne de UITextView: http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/
Malheureusement, il n'y a pas de méthode sanctionnée pour activer le crénage, je déconseillerais certainement d'utiliser un hack en utilisant un sélecteur camouflé.
Voici mon Radar, je vous suggère de le Duper: http://www.cocoanetics.com/2012/12/radar-uitextview-ignores-font-kerning/
En cela, je soutiens que lorsque vous définissez du texte sur UITextView via setAttributedText, les développeurs s'attendent à ce que le crénage soit activé par défaut car c'est ainsi qu'il correspond le mieux à la sortie du rendu du texte via CoreText.
Avez-vous jeté un oeil ici: https://github.com/AliSoftware/OHAttributedLabel ? en regardant ce projet, il semble qu'il existe plusieurs applications dans l'App store qui utilisent le balisage (fourni par cette classe). Peut-être que vous pourriez utiliser cette classe ou en tirer quelques idées d'implémentation. Peut-être que votre application telle quelle passera muster.
Avec iOS6, UITextView
a un nouvel attribut attributedText
, à laquelle vous pouvez assigner un NSAttributedString
. Je ne l'ai pas essayé, mais il serait intéressant de voir si vous obtenez le crénage que vous recherchez si vous définissez cet attribut de votre vue texte plutôt que l'attribut text
. Et il a l'avantage d'être une interface publique.
Il n'y a pas besoin de recourir à des méthodes privées. Vous pouvez facilement le simuler en changeant dynamiquement la propriété attributedText
du champ de texte chaque fois que le texte change en vous abonnant à la notification 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];
}