web-dev-qa-db-fra.com

NSAttributedString couleur d'arrière-plan et coins arrondis

J'ai une question concernant les coins arrondis et la couleur d'arrière-plan du texte pour un UIView personnalisé.

Fondamentalement, j'ai besoin d'obtenir un effet comme celui-ci (image jointe - notez les coins arrondis d'un côté) dans une UIView personnalisée: Background highlight

Je pense que l'approche à utiliser est la suivante:

  • Utilisez Core Text pour obtenir des exécutions de glyphes.
  • Vérifiez la plage de surbrillance.
  • Si l'analyse en cours se situe dans la plage de surbrillance, dessinez un rectangle d'arrière-plan avec des coins arrondis et la couleur de remplissage souhaitée avant de dessiner l'analyse de glyphe.
  • Dessinez la course de glyphe.

Cependant, je ne sais pas si c'est la seule solution (ou d'ailleurs, si c'est la solution la plus efficace).

Utiliser un UIWebView n'est pas une option, donc je dois le faire dans un UIView personnalisé.

Ma question étant, est-ce la meilleure approche à utiliser, et suis-je sur la bonne voie? Ou est-ce que je manque quelque chose d'important ou que je m'y trompe?

48
codeBearer

J'ai réussi à obtenir l'effet ci-dessus, alors j'ai pensé publier une réponse pour la même chose.

Si quelqu'un a des suggestions pour rendre cela plus efficace, n'hésitez pas à contribuer. Je ne manquerai pas de marquer votre réponse comme la bonne. :)

Pour ce faire, vous devrez ajouter un "attribut personnalisé" à NSAttributedString.

Fondamentalement, cela signifie que vous pouvez ajouter n'importe quelle paire clé-valeur, tant que c'est quelque chose que vous pouvez ajouter à une instance NSDictionary. Si le système ne reconnaît pas cet attribut, il ne fait rien. C'est à vous, en tant que développeur, de fournir une implémentation et un comportement personnalisés pour cet attribut.

Aux fins de cette réponse, supposons que j'ai ajouté un attribut personnalisé appelé: @"MyRoundedBackgroundColor" Avec une valeur de [UIColor greenColor].

Pour les étapes qui suivent, vous aurez besoin d'avoir une compréhension de base de la façon dont CoreText fait avancer les choses. Consultez Guide de programmation du texte de base d'Apple pour comprendre ce qu'est un cadre/une ligne/un glyphe/un glyphe, etc.

Voici donc les étapes:

  1. Créez une sous-classe UIView personnalisée.
  2. Avoir une propriété pour accepter un NSAttributedString.
  3. Créez un CTFramesetter en utilisant cette instance NSAttributedString.
  4. Remplacez la méthode drawRect:
  5. Créez une instance CTFrame à partir de CTFramesetter.
    1. Vous devrez donner un CGPathRef pour créer le CTFrame. Faites en sorte que CGPath soit le même que le cadre dans lequel vous souhaitez dessiner le texte.
  6. Obtenez le contexte graphique actuel et retournez le système de coordonnées texte.
  7. En utilisant CTFrameGetLines(...), obtenez toutes les lignes dans le CTFrame que vous venez de créer.
  8. En utilisant CTFrameGetLineOrigins(...), obtenez toutes les origines de ligne pour CTFrame.
  9. Commencez un for loop - pour chaque ligne du tableau de CTLine...
  10. Définissez la position du texte au début de CTLine à l'aide de CGContextSetTextPosition(...).
  11. En utilisant CTLineGetGlyphRuns(...) récupérez toutes les exécutions de glyphes (CTRunRef) à partir de CTLine.
  12. Démarrez un autre for loop - pour chaque glyphRun dans le tableau de CTRun...
  13. Obtenez la plage de l'exécution en utilisant CTRunGetStringRange(...).
  14. Obtenez des limites typographiques en utilisant CTRunGetTypographicBounds(...).
  15. Obtenez le décalage x pour l'exécution à l'aide de CTLineGetOffsetForStringIndex(...).
  16. Calculez le rectangle englobant (appelons-le runBounds) en utilisant les valeurs renvoyées par les fonctions susmentionnées.
    1. Rappelez-vous - CTRunGetTypographicBounds(...) nécessite des pointeurs vers des variables pour stocker la "montée" et la "descente" du texte. Vous devez les ajouter pour obtenir la hauteur de course.
  17. Obtenez les attributs de l'exécution à l'aide de CTRunGetAttributes(...).
  18. Vérifiez si le dictionnaire d'attributs contient votre attribut.
  19. Si votre attribut existe, calculez les limites du rectangle à peindre.
  20. Le texte de base a les origines de la ligne à la ligne de base. Nous devons dessiner du point le plus bas du texte au point le plus haut. Ainsi, nous devons nous ajuster à la descente.
  21. Donc, soustrayez la descente du rectangle englobant que nous avons calculé à l'étape 16 (runBounds).
  22. Maintenant que nous avons le runBounds, nous savons quelle zone nous voulons peindre - maintenant nous pouvons utiliser n'importe laquelle des méthodes CoreGraphis/UIBezierPath pour dessiner et remplir un rect avec des coins arrondis.
    1. UIBezierPath a une méthode de classe de commodité appelée bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: qui vous permet de contourner des coins spécifiques. Vous spécifiez les coins à l'aide de masques de bits dans le 2e paramètre.
  23. Maintenant que vous avez rempli le rect, dessinez simplement la course de glyphe en utilisant CTRunDraw(...).
  24. Célébrez la victoire pour avoir créé votre attribut personnalisé - buvez une bière ou quelque chose! :RÉ

En ce qui concerne la détection de l'extension de la plage d'attributs sur plusieurs exécutions, vous pouvez obtenir l'intégralité de la plage effective de votre attribut personnalisé lorsque la première exécution rencontre l'attribut. Si vous constatez que la longueur de la plage efficace maximale de votre attribut est supérieure à la longueur de votre course, vous devez peindre des angles vifs sur le côté droit (pour un script de gauche à droite). Plus de mathématiques vous permettront également de détecter le style de coin en surbrillance pour la ligne suivante. :)

Ci-joint une capture d'écran de l'effet. La boîte en haut est un UITextView standard, pour lequel j'ai défini l'attributText. La case en bas est celle qui a été implémentée à l'aide des étapes ci-dessus. La même chaîne attribuée a été définie pour les deux textViews. custom attribute with rounded corners

Encore une fois, s'il existe une meilleure approche que celle que j'ai utilisée, faites-le moi savoir! :RÉ

J'espère que cela aide la communauté. :)

À votre santé!

55
codeBearer