web-dev-qa-db-fra.com

Avec quoi devrais-je remplacer la méthode obsolète sizeWithFont:?

J'ai une méthode qui me donne la taille parfaite pour un UITextView étant donné une longueur de chaîne (avec la taille de police correcte correspondante):

- (NSInteger) heightOfLabel:(NSString*) string {
    CGSize maximumLabelSize = CGSizeMake([[UIScreen mainScreen] bounds].size.width - 40, FLT_MAX);
    CGSize expectedLabelSize = [[NSString stringTrimmedForLeadingAndTrailingWhiteSpacesFromString:string]
             sizeWithFont:[UIFont systemFontOfSize:15]
             constrainedToSize:maximumLabelSize 
             lineBreakMode:NSLineBreakByWordWrapping];

    return expectedLabelSize.height + 5;
}

En fait, cela me donne toujours un ajustement parfait, même dans iOS7. Bien que maintenant, il propose une méthode d'avertissement qui dit que je ne devrais pas utiliser 'sizeWithFont: contrainedToSize: lineBreakMode'.

Il dit maintenant que je devrais utiliser -boundingRectWithSize: options: attributes: context:

Cette méthode n'est pas nouvelle pour iOS7 et je pense donc qu'il est correct de la demander en cas de débordement de pile, plutôt que de passer au forum officiel des développeurs Apple Apple).

J'ai trois questions:

1) Parce qu'il est obsolète, cela signifie-t-il que je devrais définitivement le remplacer, même s'il fonctionne toujours?

2) J'ai essayé de nombreuses méthodes boundingRectWithSize différentes: avec diverses variables mais ce n'est jamais parfait, il semble toujours être légèrement en dehors (comme le soulignent de nombreuses questions de stackoverflow) - y a-t-il un remplacement parfait avec cette méthode non obsolète qui fait exactement la même que ma méthode précédente avec un minimum de tracas?

3) pourquoi supprimer cette méthode? Est-ce à cause du chevauchement avec cette autre méthode?

26
Rambatino

Après une heure d'erreur d'essai, j'ai réussi à le faire fonctionner:

CGSize maximumLabelSize = CGSizeMake(tableView.width, MAXFLOAT);

NSStringDrawingOptions options = NSStringDrawingTruncatesLastVisibleLine |
                                 NSStringDrawingUsesLineFragmentOrigin;

NSDictionary *attr = @{NSFontAttributeName: [UIFont systemFontOfSize:15]};
CGRect labelBounds = [string boundingRectWithSize:maximumLabelSize 
                                          options:options
                                       attributes:attr
                                          context:nil];

Mise à jour:

Comme le mentionne M. T dans la réponse ci-dessous: dans iOS 7 et versions ultérieures, cette méthode renvoie des tailles fractionnaires (dans le composant de taille du CGRect renvoyé); pour utiliser une taille renvoyée dans les vues de taille, vous devez utiliser augmenter sa valeur à l'entier supérieur le plus proche en utilisant la fonction ceil.ceilf, il est recommandé d'utiliser la fonction.

CGFloat height = ceilf(labelBounds.size.height);
57
Rambatino

Je crois que la fonction était obsolète parce que cette série de fonctions NSString + UIKit était basée sur la bibliothèque UIStringDrawing, 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 fonctionnalité UIKit), 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 la méthode boundingRectWithSize:... Pour NSAttributedStrings. Cela a été construit au-dessus des bibliothèques NSStringDrawing et est thread-safe.

Si vous regardez la nouvelle fonction NSString boundingRectWithSize:..., Elle demande un tableau d'attributs de la même manière qu'un NSAttributeString. Si je devais deviner, cette nouvelle fonction NSString dans iOS 7 est simplement un wrapper pour la fonction NSAttributeString d'iOS 6.

Sur cette note, si vous ne preniez en charge que iOS 6 et iOS 7, je changerais certainement tous les sizeWithFont:... De votre NSString en boundingRectWithSize de NSAttributeString. Cela vous évitera beaucoup de maux de tête si vous avez un étui d'angle multi-threading étrange! Voici comment j'ai converti le sizeWithFont:constrainedToSize: De NSString:

Ce qui était autrefois:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font 
               constrainedToSize:CGSizeMake(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:CGSizeMake(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 CGRect renvoyé); pour utiliser une taille renvoyé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);
13
Mr. T

Pour les problèmes de saut de ligne:

- (CGFloat)heightNeededForText:(NSString *)text withFont:(UIFont *)font width:(CGFloat)width lineBreakMode:(NSLineBreakMode)lineBreakMode {
    NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        paragraphStyle.lineBreakMode = lineBreakMode;
    CGSize size = [text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
                                     options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                  attributes:@{ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle }
                                     context:nil].size;

    return ceilf(size.height);
}
5
Alexander of Norway

Version rapide de la réponse d'Alexandre de Norvège ...

func heightNeededForText(text: NSString, withFont font: UIFont, width: CGFloat, lineBreakMode:NSLineBreakMode) -> CGFloat {
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineBreakMode = lineBreakMode
        let size: CGSize = text.boundingRectWithSize(CGSizeMake(width, CGFloat.max), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: [ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle], context: nil).size//text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MA

        return ceil(size.height);
    }

Dans le code où vous voulez obtenir la hauteur, appelez simplement la méthode comme ci-dessous ...

let size = self.heightNeededForText(text as NSString, withFont: UIFont.systemFontOfSize(15.0), width: scrollView.frame.size.width - 20, lineBreakMode: NSLineBreakMode.ByWordWrapping) //Can edit the font size and LinebreakMode
2
Koti Tummala