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:

entrez la description de l'image ici

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.

24
demandé sur Anthony Mattox 2012-11-05 23:48:40

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.

36
répondu 0xced 2012-11-14 21:43:31

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.

1
répondu rob mayoff 2012-11-15 06:36:11

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.

1
répondu Cocoanetics 2012-12-16 18:54:46

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.

0
répondu Mark DiGiovanni 2012-11-09 15:48:06

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.

0
répondu Mark Granoff 2012-11-14 21:51:30

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];
}
-1
répondu boliva 2014-03-14 15:42:43