Je souhaite dessiner NSAttributedStrings dans des boîtes de largeur fixe, mais je ne parviens pas à calculer la hauteur correcte à prendre lors du dessin. Jusqu'à présent, j'ai essayé:
Appeler - (NSSize) size
, mais les résultats sont inutiles (dans ce but), car ils donneront la largeur souhaitée par la chaîne.
Appelez - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
avec un rectangle dont la largeur correspond à celle que je souhaite et NSStringDrawingUsesLineFragmentOrigin
dans les options, exactement comme dans mon dessin. Les résultats sont ... difficiles à comprendre; certainement pas ce que je cherche. (Comme indiqué dans plusieurs endroits, y compris this Cocoa-Dev thread).
Créer un NSTextView temporaire et faire:[[tmpView textStorage] setAttributedString:aString];
[tmpView setHorizontallyResizable:NO];
[tmpView sizeToFit];
Lorsque j'interroge le cadre de tmpView, la largeur est toujours celle souhaitée et la hauteur est souvent correcte ... jusqu'à obtenir des chaînes plus longues, qui correspondent souvent à la moitié de la taille requise. (Il ne semble pas y avoir de taille maximale atteinte: une image aura une hauteur de 273,0 (environ 300 trop courte), l’autre sera de 478,0 (seulement 60 trop trop courte)).
J'apprécierais tous les conseils, si quelqu'un d'autre a réussi cela.
-[NSAttributedString boundingRectWithSize:options:]
Vous pouvez spécifier NSStringDrawingUsesDeviceMetrics
pour obtenir l'union de toutes les limites de glyphes .
Contrairement à -[NSAttributedString size]
, la NSRect
renvoyée représente les dimensions de la zone qui changeraient si la chaîne est dessinée.
Comme @Bryan commente, boundingRectWithSize:options:
est obsolète (non recommandé) sous OS X 10.11 et versions ultérieures. En effet, le style des chaînes est désormais dynamique en fonction du contexte.
Pour OS X 10.11 et les versions ultérieures, consultez la documentation de Calculating Text Height de Apple.
Vous pourriez être intéressé par la grande catégorie NS(Attributed)String+Geometrics
((sous OS X uniquement) de Jerry Krinock, conçue pour effectuer toutes sortes de mesures de chaîne, y compris ce que vous recherchez.
J'ai une chaîne complexe d'attributs avec plusieurs polices et j'ai obtenu des résultats incorrects avec certaines des réponses ci-dessus que j'ai essayées en premier. L'utilisation d'une UITextView m'a donné la hauteur correcte, mais était trop lente pour mon cas d'utilisation (dimensionnement des cellules de collecte). J'ai écrit le code Swift en utilisant la même approche générale que celle décrite dans le document Apple référencé précédemment et décrit par Erik. Cela m'a donné des résultats corrects avec une exécution plus rapide que d'avoir un UITextView faire le calcul.
private func heightForString(_ str : NSAttributedString, width : CGFloat) -> CGFloat {
let ts = NSTextStorage(attributedString: str)
let size = CGSize(width:width, height:CGFloat.greatestFiniteMagnitude)
let tc = NSTextContainer(size: size)
tc.lineFragmentPadding = 0.0
let lm = NSLayoutManager()
lm.addTextContainer(tc)
ts.addLayoutManager(lm)
lm.glyphRange(forBoundingRect: CGRect(Origin: .zero, size: size), in: tc)
let rect = lm.usedRect(for: tc)
return rect.integral.size.height
}
Swift 3:
let attributedStringToMeasure = NSAttributedString(string: textView.text, attributes: [
NSFontAttributeName: UIFont(name: "GothamPro-Light", size: 15)!,
NSForegroundColorAttributeName: ClickUpConstants.defaultBlackColor
])
let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: widthOfActualTextView, height: 10))
placeholderTextView.attributedText = attributedStringToMeasure
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))
height = size.height
Cette réponse fonctionne très bien pour moi, contrairement aux autres qui me donnaient des hauteurs incorrectes pour les grandes chaînes.
Si vous voulez faire cela avec du texte normal au lieu du texte attribué, procédez comme suit:
let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: ClickUpConstants.screenWidth - 30.0, height: 10))
placeholderTextView.text = "Some text"
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))
height = size.height
Sous OS X 10.11+, la méthode suivante fonctionne pour moi (à partir de Calcul de la hauteur du texte document)
- (CGFloat)heightForString:(NSAttributedString *)myString atWidth:(float)myWidth
{
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:myString];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:
NSMakeSize(myWidth, FLT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
[layoutManager glyphRangeForTextContainer:textContainer];
return [layoutManager
usedRectForTextContainer:textContainer].size.height;
}
Utiliser la méthode NSAttributedString
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context
La taille est la contrainte sur la surface, la largeur de la surface calculée est limitée à la largeur spécifiée alors que la hauteur est flexible en fonction de cette largeur. On peut spécifier nil pour contexte si ce n'est pas disponible. Pour obtenir une taille de texte multiligne, utilisez NSStringDrawingUsesLineFragmentOrigin pour les options.
Je viens de perdre beaucoup de temps à ce sujet, alors je fournis une réponse supplémentaire pour en sauver d'autres à l'avenir. La réponse de Graham est correcte à 90%, mais il lui manque un élément clé:
Pour obtenir des résultats précis avec -boundingRectWithSize:options:
vous DEVEZ passer les options suivantes :
NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading`
Si vous omettez celui de lineFragmentOrigin, vous obtiendrez un non-sens; le rect retourné aura une hauteur d'une seule ligne et ne respectera pas du tout la taille passée dans la méthode.
Pourquoi ceci est si compliqué et si mal documenté me dépasse. Mais là vous l'avez. Passez ces options et cela fonctionnera parfaitement (sous OS X au moins).
Aucune réponse sur cette page n'a fonctionné pour moi, pas plus que l'ancien code Objective-C de la documentation d'Apple . Ce que j'ai finalement réussi à faire pour un UITextView
est tout d'abord de définir sa propriété text
ou attributedText
puis de calculer la taille requise comme ceci:
let size = textView.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.max))
Marche parfaitement. Booyah!
Comme beaucoup de gars mentionnés ci-dessus, et base sur mon test.
J'utilise open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect
sur iOS comme ceci:
let rect = attributedTitle.boundingRect(with: CGSize(width:200, height:0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)
Ici le200
est la largeur fixe comme vous le souhaitez, height Je lui donne 0 car je pense qu'il est préférable de dire que la hauteur de l'API est unlimited .
L'option n'est pas si importante ici , J'ai essayé .usesLineFragmentOrigin
ou .usesLineFragmentOrigin.union(.usesFontLeading)
ou .usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics)
, cela donne le même résultat.
Et le résultat est attendu comme si c'était mon cas.
Merci.
J'ai trouvé la classe d'assistance pour trouver la hauteur et la largeur de attributText (code testé)
https://Gist.github.com/azimin/aa1a79aefa1cec031152fa63401d2292
Ajouter le fichier ci-dessus dans votre projet
Comment utiliser
let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
let width : CGFloat = attribString.calculatedTextWidth()
print("width is :: >> \(width)")