[Swift][RESOLU][Affichage d'une NSAttributedString avec une contrainte de largeur]
Je cherche à calculer la taille nécessaire à l'affichage d'une NSAttributedString, avec une contrainte de largeur. Il y a une méthode pour ça dans le SDK, que j'ai utilisé pour écrire une extension :
extension NSAttributedString {
func sizeContrainteH(contrainteH:CGFloat) -> CGSize {
let rect = self.boundingRectWithSize(CGSizeMake(contrainteH, CGFloat.max),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
context:nil)
return CGSizeMake(ceil(rect.width), ceil(rect.height))
}
}
ça marche .. presque toujours ! De temps en temps, j'ai un résultat légèrement plus grand que la contrainte demandée.
Voici un code mettant le problème en évidence :
import UIKit
// Changement font
func changementFont (texte:NSAttributedString, font:UIFont) -> NSAttributedString
{
let texteMutable = NSMutableAttributedString(attributedString: texte)
texteMutable.addAttribute(NSFontAttributeName, value: font, range:NSRange(location:0, length:texte.length))
return NSAttributedString(attributedString: texteMutable)
}
// Cacul de la taille necessaire à l'affichage, avec une contrainte horizontale
extension NSAttributedString {
func sizeContrainteH(contrainteH:CGFloat) -> CGSize {
let rect = self.boundingRectWithSize(CGSizeMake(contrainteH, CGFloat.max),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
context:nil)
return CGSizeMake(ceil(rect.width), ceil(rect.height))
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let font = UIFont(name: "ArialMT", size: 18.0)
let texte1 = "Lors d'une cérémonie d'accueil dans les jardins de la Maison Blanche sous un grand soleil, M. Obama a salué la place singulière du Japon \"l'un des alliés les plus proches des Etats-Unis\". \"Notre relation bilatérale est plus forte que jamais\", a de son côté souligné M. Abe."
let texte2 = "Etats-Unis et Japon saluent par ailleurs les \"progrès significatifs\" enregistrés dans leurs discussions bilatérales en vue d'aboutir à un vaste accord de libre-échange en Asie-Pacifique qui représenterait 40% du PIB mondial et rassemblerait 12 pays, à l'exception notable de la Chine."
let texte3 = "Le secrétaire d'Etat John Kerry a réaffirmé en début de semaine que cette alliance couvrait \"tous les territoires\" sous la responsabilité du Japon, \"y compris les à®les Senkaku\"."
let attrStr1 = changementFont(NSAttributedString(string: texte1), font!)
let attrStr2 = changementFont(NSAttributedString(string: texte2), font!)
let attrStr3 = changementFont(NSAttributedString(string: texte3), font!)
let textes = [attrStr1, attrStr2, attrStr3]
for phrase in textes {
let size = phrase.sizeContrainteH(320.0)
println(size)
}
}
}
Avec une police "ArialMT" de corps 18 et une contrainte de 320.0 points, j'obtiens les tailles suivantes :
(325,0 141,0)
(320,0 161,0)
(290,0 101,0)
Le premier texte ne respecte pas la contrainte de 320.0 points.
Il suffit de changer la taille de la police pour faire apparaà®tre ou disparaitre le problème. Exemple avec de l'ArialMT corps 17 :
(307,0 133,0)
(320,0 152,0)
(324,0, 95,0)
C'est maintenant la troisième phrase qui ne respecte plus la contrainte. Grrr..
Le test fabrique les NSAttributedString avec du code, mais à l'origine cela vient d'un traitement de texte. J'ai simplifié le problème pour le reproduire dans sa forme "pur".
Quelqu'un a une idée sur l'origine du problème ?
EDIT : J'ai utilisé le terme de contrainte parce qu'il me semblait approprié, mais cela n'a rien à voir avec Storyboard et compagnie. C'est du code pur et dur, à l'ancienne.
Réponses
Ton code :
Mon code :
ça se ressemble beaucoup. Je vais quand même reprendre ton code et le retranscrire en swift, pour voir.
Les valeurs (ArialMT corps 18) sans arrondi :
324.0615234375 140.765625
319.623046875 160.875
289.9072265625 100.546875
Reprise des opérations après une pause préparation sushis pour anniversaire !
Voici ma copie de ta méthode :
Les résultats sont identiques, y compris les dépassements de contrainte !
Mon code original : (325.0, 141.0)
Ma copie de ton code : (325.0, 141.0)
Mon code original : (320.0, 161.0)
Ma copie de ton code : (320.0, 161.0)
Mon code original : (290.0, 101.0)
Ma copie de ton code : (290.0, 101.0)
Car malheureusement bien souvent les fichiers de police n'ont pas tous la même rigueur, et certains sont mal définis / configurés, du genre dans le fichier de police l'entrée qui décrit le caractère X indique qu'il fait 10 pixels alors qu'en vrai il en fait 12 une fois dessiné... ou la baseline de la police est indiquée dans le header comme étant à tel position Y, mais en fait tu te rends compte que tous les glyphes et leur dessin dans le fichier de police sont dessinés un peu plus haut ou plus pas que cette position Y, ...)
J'ai déjà eu le cas souvent pour des fichiers de polices custom (un fichier TTF que tu rajoutes dans le bundle de ton application, et qui vient parfois d'on ne sait trop où, fourni par le client mais de qualité/finition/rigueur discutable, etc), pas souvenir de l'avoir eu pour des polices natives comme ArialMT mais après tout ça ne m'étonnerait pas tant que ça...
Nouvelle version avec ajout de l'option UsesDeviceMetrics
La bonne nouvelle c'est qu'il n'y a plus de dépassement de contraintes.
La mauvaise nouvelle c'est que les résultats sont toujours nuls :
(0.0, 0.0)
(0.0, 0.0)
(0.0, 0.0)
Problème résolu en utilisant la technique préconisée par Apple à cette adresse :
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html
Test en ArialMT corps 18 (contrainte de 320.0 points)
(320.0, 145.0)
(320.0, 166.0)
(290.0, 104.0)
Test en ArialMT corps 17 :
(307.0, 137.0)
(320.0, 157.0)
(320.0, 98.0)