Dans iOS 7, sizeWithFont:
est désormais obsolète. Comment puis-je maintenant passer l'objet UIFont dans la méthode de remplacement sizeWithAttributes:
?
Dans iOS 7, sizeWithFont:
est désormais obsolète. Comment puis-je maintenant passer l'objet UIFont dans la méthode de remplacement sizeWithAttributes:
?
Réponses:
Utilisez sizeWithAttributes:
plutôt, qui prend maintenant un NSDictionary
. Passez la paire avec la clé UITextAttributeFont
et votre objet de police comme ceci:
CGSize size = [string sizeWithAttributes:
@{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];
// Values are fractional -- you should take the ceilf to get equivalent values
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));
boundingRectWithSize:options:attributes:context:
place, en passant CGSizeMake(250.0f, CGFLOAT_MAX)
dans la plupart des cas.
Je crois que la fonction était obsolète parce que cette série de NSString+UIKit
fonctions ( sizewithFont:...
, etc.) était basée sur la UIStringDrawing
bibliothèque, qui n'était pas sûre pour les threads. Si vous avez essayé de les exécuter non pas sur le thread principal (comme toute autre UIKit
fonctionnalité), vous obtiendrez des comportements imprévisibles. En particulier, si vous exécutez la fonction sur plusieurs threads simultanément, cela bloquera probablement votre application. C'est pourquoi dans iOS 6, ils ont introduit une boundingRectWithSize:...
méthode pour NSAttributedString
. Cela a été construit au-dessus des NSStringDrawing
bibliothèques et est thread-safe.
Si vous regardez la nouvelle NSString
boundingRectWithSize:...
fonction, elle demande un tableau d'attributs de la même manière qu'un a NSAttributeString
. Si je devais deviner, cette nouvelle NSString
fonction dans iOS 7 est simplement un wrapper pour la NSAttributeString
fonction d'iOS 6.
Sur cette note, si vous ne preniez en charge que iOS 6 et iOS 7, je changerais certainement tous vos fichiers NSString
sizeWithFont:...
en NSAttributeString
boundingRectWithSize
. Cela vous évitera beaucoup de maux de tête si vous avez un étui d'angle multi-threading étrange! Voici comment j'ai converti NSString
sizeWithFont:constrainedToSize:
:
Ce qui était autrefois:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font
constrainedToSize:(CGSize){width, CGFLOAT_MAX}];
Peut être remplacé par:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
[[NSAttributedString alloc] initWithString:text
attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
CGSize size = rect.size;
Veuillez noter que la documentation mentionne:
Dans iOS 7 et versions ultérieures, cette méthode renvoie des tailles fractionnaires (dans le composant de taille du retour
CGRect
); pour utiliser une taille retournée à des vues de taille, vous devez utiliser augmenter sa valeur à l'entier supérieur le plus proche à l'aide de la fonction ceil.
Donc, pour extraire la hauteur ou la largeur calculée à utiliser pour dimensionner les vues, j'utiliserais:
CGFloat height = ceilf(size.height);
CGFloat width = ceilf(size.width);
Comme vous pouvez le voir sizeWithFont
sur le site Apple Developer, il est obsolète, nous devons donc l'utiliser sizeWithAttributes
.
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
NSString *text = @"Hello iOS 7.0";
if (SYSTEM_VERSION_LESS_THAN(@"7.0")) {
// code here for iOS 5.0,6.0 and so on
CGSize fontSize = [text sizeWithFont:[UIFont fontWithName:@"Helvetica"
size:12]];
} else {
// code here for iOS 7.0
CGSize fontSize = [text sizeWithAttributes:
@{NSFontAttributeName:
[UIFont fontWithName:@"Helvetica" size:12]}];
}
[NSObject respondsToSelector:]
méthode comme ici: stackoverflow.com/a/3863039/1226304
J'ai créé une catégorie pour gérer ce problème, la voici:
#import "NSString+StringSizeWithFont.h"
@implementation NSString (StringSizeWithFont)
- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
if ([self respondsToSelector:@selector(sizeWithAttributes:)])
{
NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
return ([self sizeWithAttributes:attribs]);
}
return ([self sizeWithFont:fontToUse]);
}
De cette façon, vous n'avez qu'à trouver / remplacer sizeWithFont:
par sizeWithMyFont:
et vous êtes prêt à partir.
Dans iOS7, j'avais besoin de la logique pour renvoyer la hauteur correcte pour la méthode tableview: heightForRowAtIndexPath, mais la tailleWithAttributes renvoie toujours la même hauteur quelle que soit la longueur de la chaîne car elle ne sait pas qu'elle va être placée dans une cellule de tableau à largeur fixe . J'ai trouvé que cela fonctionne très bien pour moi et calcule la bonne hauteur en tenant compte de la largeur de la cellule du tableau! Ceci est basé sur la réponse de M. T ci-dessus.
NSString *text = @"The text that I want to wrap in a table cell."
CGFloat width = tableView.frame.size.width - 15 - 30 - 15; //tableView width - left border width - accessory indicator - right border width
UIFont *font = [UIFont systemFontOfSize:17];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
CGSize size = rect.size;
size.height = ceilf(size.height);
size.width = ceilf(size.width);
return size.height + 15; //Add a little more padding for big thumbs and the detailText label
Les étiquettes multilignes utilisant la hauteur dynamique peuvent nécessiter des informations supplémentaires pour définir correctement la taille. Vous pouvez utiliser sizeWithAttributes avec UIFont et NSParagraphStyle pour spécifier à la fois la police et le mode de saut de ligne.
Vous définiriez le style de paragraphe et utiliseriez un NSDictionary comme ceci:
// set paragraph style
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:NSLineBreakByWordWrapping];
// make dictionary of attributes with paragraph style
NSDictionary *sizeAttributes = @{NSFontAttributeName:myLabel.font, NSParagraphStyleAttributeName: style};
// get the CGSize
CGSize adjustedSize = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);
// alternatively you can also get a CGRect to determine height
CGRect rect = [myLabel.text boundingRectWithSize:adjustedSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:sizeAttributes
context:nil];
Vous pouvez utiliser la propriété CGSize 'adjustSize' ou CGRect comme propriété rect.size.height si vous recherchez la hauteur.
Plus d'informations sur NSParagraphStyle ici: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html
// max size constraint
CGSize maximumLabelSize = CGSizeMake(184, FLT_MAX)
// font
UIFont *font = [UIFont fontWithName:TRADE_GOTHIC_REGULAR size:20.0f];
// set paragraph style
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
// dictionary of attributes
NSDictionary *attributes = @{NSFontAttributeName:font,
NSParagraphStyleAttributeName: paragraphStyle.copy};
CGRect textRect = [string boundingRectWithSize: maximumLabelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes
context:nil];
CGSize expectedLabelSize = CGSizeMake(ceil(textRect.size.width), ceil(textRect.size.height));
Créez une fonction qui prend une instance UILabel. et retourne CGSize
CGSize constraint = CGSizeMake(label.frame.size.width , 2000.0);
// Adjust according to requirement
CGSize size;
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){
NSRange range = NSMakeRange(0, [label.attributedText length]);
NSDictionary *attributes = [label.attributedText attributesAtIndex:0 effectiveRange:&range];
CGSize boundingBox = [label.text boundingRectWithSize:constraint options: NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
}
else{
size = [label.text sizeWithFont:label.font constrainedToSize:constraint lineBreakMode:label.lineBreakMode];
}
return size;
tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Solution alternative-
CGSize expectedLabelSize;
if ([subTitle respondsToSelector:@selector(sizeWithAttributes:)])
{
expectedLabelSize = [subTitle sizeWithAttributes:@{NSFontAttributeName:subTitleLabel.font}];
}else{
expectedLabelSize = [subTitle sizeWithFont:subTitleLabel.font constrainedToSize:subTitleLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping];
}
S'appuyant sur @bitsand, il s'agit d'une nouvelle méthode que je viens d'ajouter à ma catégorie NSString + Extras:
- (CGRect) boundingRectWithFont:(UIFont *) font constrainedToSize:(CGSize) constraintSize lineBreakMode:(NSLineBreakMode) lineBreakMode;
{
// set paragraph style
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:lineBreakMode];
// make dictionary of attributes with paragraph style
NSDictionary *sizeAttributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName: style};
CGRect frame = [self boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:sizeAttributes context:nil];
/*
// OLD
CGSize stringSize = [self sizeWithFont:font
constrainedToSize:constraintSize
lineBreakMode:lineBreakMode];
// OLD
*/
return frame;
}
J'utilise juste la taille de l'image résultante.
Vous pouvez toujours utiliser sizeWithFont
. mais, dans la méthode iOS> = 7.0, le blocage se produit si la chaîne contient des espaces ou des lignes de fin et de début \n
.
Découper du texte avant de l'utiliser
label.text = [label.text stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
Cela peut également s'appliquer à sizeWithAttributes
et [label sizeToFit]
.
aussi, chaque fois que vous avez un nsstringdrawingtextstorage message sent to deallocated instance
appareil iOS 7.0, il traite cela.
Mieux utiliser les dimensions automatiques (Swift):
tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension
NB: 1. Le prototype UITableViewCell doit être correctement conçu (pour l'instance, n'oubliez pas de définir UILabel.numberOfLines = 0, etc.) 2. Supprimer la méthode HeightForRowAtIndexPath
VIDÉO: https://youtu.be/Sz3XfCsSb6k
La réponse acceptée dans Xamarin serait (utilisez sizeWithAttributes et UITextAttributeFont):
UIStringAttributes attributes = new UIStringAttributes
{
Font = UIFont.SystemFontOfSize(17)
};
var size = text.GetSizeUsingAttributes(attributes);
Comme la réponse @Ayush:
Comme vous pouvez le voir
sizeWithFont
sur le site Apple Developer, il est obsolète, nous devons donc l'utilisersizeWithAttributes
.
Eh bien, en supposant qu'en 2019+, vous utilisez probablement Swift et String
au lieu d'Objective-c et NSString
, voici la bonne façon d'obtenir la taille d'un String
avec une police prédéfinie:
let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])
- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
if ([self respondsToSelector:@selector(sizeWithAttributes:)])
{
NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
return ([self sizeWithAttributes:attribs]);
}
return ([self sizeWithFont:fontToUse]);
}
Voici l'équivalent monotouch si quelqu'un en a besoin:
/// <summary>
/// Measures the height of the string for the given width.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="width">The width.</param>
/// <param name="padding">The padding.</param>
/// <returns></returns>
public static float MeasureStringHeightForWidth(this string text, UIFont font, float width, float padding = 20)
{
NSAttributedString attributedString = new NSAttributedString(text, new UIStringAttributes() { Font = font });
RectangleF rect = attributedString.GetBoundingRect(new SizeF(width, float.MaxValue), NSStringDrawingOptions.UsesLineFragmentOrigin, null);
return rect.Height + padding;
}
qui peut être utilisé comme ceci:
public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
//Elements is a string array
return Elements[indexPath.Row].MeasureStringHeightForWidth(UIFont.SystemFontOfSize(UIFont.LabelFontSize), tableView.Frame.Size.Width - 15 - 30 - 15);
}
CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;
Essayez cette syntaxe:
NSAttributedString *attributedText =
[[NSAttributedString alloc] initWithString:text
attributes:@{NSFontAttributeName: font}];
Rien de tout cela n'a fonctionné pour moi dans iOS 7. Voici ce que j'ai fini par faire. Je mets cela dans ma classe de cellule personnalisée et appelle la méthode dans ma méthode heightForCellAtIndexPath.
Ma cellule ressemble à la cellule de description lors de l'affichage d'une application dans l'App Store.
Tout d'abord dans le storyboard, définissez votre étiquette sur 'AttribedText', définissez le nombre de lignes sur 0 (ce qui redimensionnera automatiquement l'étiquette (iOS 6+ uniquement)) et définissez-le sur un retour à la ligne.
Ensuite, je viens d'ajouter toutes les hauteurs du contenu de la cellule dans ma classe de cellule personnalisée. Dans mon cas, j'ai une étiquette en haut qui dit toujours "Description" (_descriptionHeadingLabel), une étiquette plus petite de taille variable qui contient la description réelle (_descriptionLabel) une contrainte du haut de la cellule à l'en-tête (_descriptionHeadingLabelTopConstraint) . J'ai également ajouté 3 pour espacer un peu le bas (environ la même quantité de pomme place sur la cellule de type de sous-titre).
- (CGFloat)calculateHeight
{
CGFloat width = _descriptionLabel.frame.size.width;
NSAttributedString *attributedText = _descriptionLabel.attributedText;
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];
return rect.size.height + _descriptionHeadingLabel.frame.size.height + _descriptionHeadingLabelTopConstraint.constant + 3;
}
Et dans mon délégué Table View:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
if (indexPath.row == 0) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"descriptionCell"];
DescriptionCell *descriptionCell = (DescriptionCell *)cell;
NSString *text = [_event objectForKey:@"description"];
descriptionCell.descriptionLabel.text = text;
return [descriptionCell calculateHeight];
}
return 44.0f;
}
Vous pouvez modifier l'instruction if pour qu'elle soit un peu plus «intelligente» et obtenir réellement l'identifiant de cellule à partir d'une sorte de source de données. Dans mon cas, les cellules vont être codées en dur car il y en aura un nombre fixe dans un ordre spécifique.
boundingRectWithSize
dans ios 9.2 présente des problèmes, les résultats sont différents de ios <9.2. Vous avez trouvé ou connaissez une autre meilleure façon de procéder.
NSString
et unUILabel
(pas TOUJOURS le cas, mais souvent), pour éviter la duplication de code / etc., vous pouvez également remplacer[UIFont systemFontOfSize:17.0f]
parlabel.font
- aide à la maintenance du code en référençant les données existantes par rapport à vous en les tapant plusieurs fois ou en référençant des constantes partout dans le place, etc