web-dev-qa-db-fra.com

Le dimensionnement des polices personnalisées dans les classes de taille Xcode 6 ne fonctionne pas correctement avec les polices personnalisées

Xcode 6 a une nouvelle fonctionnalité où les polices et les tailles de police dans UILabel, UITextField et UIButton peuvent être définies automatiquement en fonction de la classe de taille de la configuration actuelle de l'appareil, directement dans le storyboard. Par exemple, vous pouvez définir un UILabel pour utiliser la taille de police 12 sur les configurations "toute largeur, hauteur compacte" (comme sur les iPhones en paysage) et la taille 18 sur les configurations "largeur régulière, hauteur régulière" (comme sur les iPad). Plus d'informations sont disponibles ici:

https://developer.Apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/Size-ClassSpecificLayout.html

Il s'agit d'une fonctionnalité vraiment géniale en théorie, car elle pourrait rendre inutile la définition par programme de polices différentes sur les fonctionnalités de l'interface utilisateur en fonction de la configuration du périphérique. À l'heure actuelle, j'ai du code conditionnel qui définit les polices en fonction du type d'appareil, mais cela signifie évidemment que je dois définir les polices par programme partout dans l'application. Donc, j'étais initialement très excité par cette fonctionnalité, mais j'ai trouvé qu'elle avait un sérieux problème d'utilisation réelle pour moi (peut-être un bug). Notez que je construis contre SDK 8 et que je fixe une cible de déploiement minimale pour iOS 8, cela n'a donc rien à voir avec la compatibilité avec les anciennes versions d'iOS.

Le problème est le suivant: si je définit des tailles de police différentes pour différentes classes de taille et que j'utilise la police "System" fournie par iOS, tout fonctionne comme prévu et les tailles de police changent en fonction de la classe de taille. Si j'utilise une police personnalisée fournie par mon application (oui, je l'ai configurée correctement dans mon ensemble d'applications, car elle fonctionne par programme) et que je définis la police personnalisée sur une étiquette dans un storyboard XCode 6, qui fonctionne également comme prévu. Mais lorsque j'essaie d'utiliser différentes tailles de police personnalisées pour différentes classes de taille, dans le storyboard, cela ne fonctionne soudainement pas. La seule différence de configuration est la police que j'ai choisie (une police personnalisée par rapport à la police système). Au lieu de cela, toutes les polices s'affichent sur l'appareil et le simulateur en tant que police système par défaut à la taille par défaut, quelle que soit la classe de taille (et j'ai vérifié via le débogueur qu'elle remplace la police système par la police réelle spécifiée dans le storyboard) . Donc, fondamentalement, la fonctionnalité de classe de taille semble être rompue pour les polices personnalisées. De plus, il est intéressant de noter que les polices personnalisées s'affichent et ajustent la taille correctement dans le volet "Aperçu" du XCode 6 pour le contrôleur de vue: ce n'est que lors de l'exécution sur le système iOS réel qu'il cesse de fonctionner (ce qui me fait penser que je le configure correctement).

J'ai essayé plusieurs polices personnalisées différentes et cela ne semble fonctionner pour aucune d'entre elles, mais cela fonctionne toujours si j'utilise plutôt "Système".

Quoi qu'il en soit, quelqu'un d'autre a-t-il vu ce problème dans Xcode 6? Avez-vous des idées pour savoir s'il s'agit d'un bogue dans iOS 8, Xcode ou quelque chose que je fais mal? La seule solution de contournement que j'ai trouvée, comme je l'ai dit, est de continuer à définir par programme les polices comme je l'ai été pour environ 3 versions d'iOS, car cela fonctionne. Mais j'aimerais pouvoir utiliser cette fonctionnalité si je pouvais la faire fonctionner avec des polices personnalisées. L'utilisation de la police système n'est pas acceptable pour notre conception.


INFORMATION ADDITIONNELLE: Depuis Xcode 8.0, le bug est corrigé.

103
mnemia

Correction rapide:

1) Définir les polices comme système pour les classes de taille

Label attributes inspector

2) Sous-classe UILabel et remplacer la méthode "layoutSubviews" comme:

- (void)layoutSubviews
{
  [super layoutSubviews];

   // Implement font logic depending on screen size
    if ([self.font.fontName rangeOfString:@"bold" options:NSCaseInsensitiveSearch].location == NSNotFound) {
        NSLog(@"font is not bold");
        self.font = [UIFont fontWithName:@"Custom regular Font" size:self.font.pointSize];
    } else {
        NSLog(@"font is bold");
        self.font = [UIFont fontWithName:@"Custom bold Font" size:self.font.pointSize];
    }

}

Soit dit en passant, c'est une technique très pratique pour les polices emblématiques

40
razor28

Après avoir tout essayé, j'ai finalement opté pour une combinaison des solutions ci-dessus. En utilisant Xcode 7.2, Swift 2.

import UIKit

class LabelDeviceClass : UILabel {

    @IBInspectable var iPhoneSize:CGFloat = 0 {
        didSet {
            if isPhone() {
                overrideFontSize(iPhoneSize)
            }
        }
    }

    @IBInspectable var iPadSize:CGFloat = 0 {
        didSet {
            if isPad() {
                overrideFontSize(iPadSize)
            }
        }
    }

    func isPhone() -> Bool {
        // return UIDevice.currentDevice().userInterfaceIdiom == .Phone
        return !isPad()
    }

    func isPad() -> Bool {
        // return UIDevice.currentDevice().userInterfaceIdiom == .Pad
        switch (UIScreen.mainScreen().traitCollection.horizontalSizeClass, UIScreen.mainScreen().traitCollection.verticalSizeClass) {
        case (.Regular, .Regular):
            return true
        default:
            return false
        }
    }

    func overrideFontSize(fontSize:CGFloat){
        let currentFontName = self.font.fontName
        if let calculatedFont = UIFont(name: currentFontName, size: fontSize) {
            self.font = calculatedFont
        }
    }

}
  • @IBInspectable Vous permet de définir la taille de la police dans le Storyboard
  • Il utilise un observateur didSet, pour éviter les pièges de layoutSubviews() (boucle infinie pour les hauteurs de ligne de la table dynamique) et awakeFromNib() (voir le commentaire de @ cocoaNoob)
  • Il utilise des classes de taille plutôt que l'idiome de l'appareil, dans l'espoir de pouvoir éventuellement l'utiliser avec @IBDesignable
  • Malheureusement, @IBDesignable Ne fonctionne pas avec traitCollection selon cet autre article de pile
  • L'instruction de changement de collection de traits est exécutée sur UIScreen.mainScreen() plutôt que self par ceci article de pile
17
Robert Chen

Solution pour UILabel: conservez la même taille de police sur toutes les classes de taille, mais modifiez la hauteur de votre étiquette en conséquence dans chaque classe de taille. Le rétrécissement automatique doit être activé sur votre étiquette. Cela a bien fonctionné dans mon cas.

9
Phil

Ce bogue (et d'autres liés à Xcode - Size Classes) m'a causé un sérieux chagrin récemment car j'ai dû parcourir un énorme fichier de storyboard pour pirater les choses.

Pour toute autre personne dans cette position, je voudrais ajouter quelque chose en plus de la réponse de @ razor28 pour soulager la douleur.

Dans le fichier d'en-tête de votre sous-classe personnalisée, utilisez IBInspectable pour vos attributs d'exécution. Cela rendra ces attributs accessibles à partir de l '"Inspecteur des attributs", visuellement juste au-dessus de la position par défaut pour les paramètres de police.

Exemple d'utilisation:

@interface MyCustomLabel : UILabel

    @property (nonatomic) IBInspectable NSString *fontType;
    @property (nonatomic) IBInspectable CGFloat iphoneFontSize;
    @property (nonatomic) IBInspectable CGFloat ipadFontSize;

@end

Cela produira très utilement cette sortie:

enter image description here

Un avantage supplémentaire est que nous n'avons plus à ajouter manuellement les attributs d'exécution pour chaque étiquette. C'est le plus proche que je pouvais obtenir du comportement prévu de XCode. Espérons qu'un correctif correct soit en cours avec iOS 9 cet été.

8
joakim

Solution similaire à celle de @ razor28 mais je pense un peu plus universelle. Fonctionne également très bien sur les anciennes versions d'iOS

https://Gist.github.com/softmaxsg/8a3ce30331f9f07f023e

3
Vitaly

Le bogue est toujours valide dans XCode 7.0 GM.

La solution de Razor28 provoque des boucles infinies dans certains cas. Mon expérience a été de l'utiliser en conjonction avec SwipeView .

Au lieu de cela, je vous suggère de:

1) Sous-classe UILabel et surcharge setFont:

- (void)setFont:(UIFont *)font
{
    font = [UIFont fontWithName:(@"Montserrat") size:font.pointSize];
    [super setFont:font];
}

2) Définissez la classe personnalisée de vos UILabels puis définissez les classes de taille de police en utilisant la police système

enter image description here

3
Foti Dim

Le problème est toujours là que vous ne pouvez pas utiliser la fonctionnalité pour définir des polices pour différentes classes de taille à partir du générateur d'interface.

Définissez simplement la police en fonction de l'appareil que vous souhaitez, comme ci-dessous:

if (Your Device is iPAd) //For iPad
{
   [yourLabel setFont:[UIFont fontWithName:@"FontName" size:FontSize]]; 
}
else  //For Other Devices then iPad
{
   [yourLabel setFont:[UIFont fontWithName:@"FontName" size:FontSize]]; 
}

Cela fonctionne parfaitement sur tous les appareils.

0
Nirmit Dagly

Toujours pas de bonne réponse signée. Ce code fonctionne bien pour moi. Vous devez d'abord désactiver la taille de police pour les classes de taille dans le générateur d'interface. Dans IB, vous pouvez utiliser une police personnalisée.

- (void) traitCollectionDidChange: (UITraitCollection *) previousTraitCollection {
    [super traitCollectionDidChange: previousTraitCollection];

    if ((self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass)
        || self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass) {

         self.textField.font = [UIFont fontWithName:textField.font.fontName size:17.f];

    }
}
0
Max Smirnov

Une combinaison de certaines des réponses ci-dessus a été utile. Voici comment j'ai résolu le bug IB via une extension Swift UILabel:

import UIKit

// This extension is only required as a work-around to an interface builder bug in XCode 7.3.1
// When custom fonts are set per size class, they are reset to a small system font
// In order for this extension to work, you must set the fonts in IB to System
// We are switching any instances of ".SFUIDisplay-Bold" to "MuseoSans-700" and ".SFUIDisplay-Regular" to "MuseoSans-300" and keeping the same point size as specified in IB

extension UILabel {
    override public func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)

        if ((traitCollection.verticalSizeClass != previousTraitCollection?.verticalSizeClass) || traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass) {
            //let oldFontName = "\(font.fontName)-\(font.pointSize)"

            if (font.fontName == systemFontRegular) {
                font = UIFont(name: customFontRegular, size: (font?.pointSize)!)
                //xlog.debug("Old font: \(oldFontName) -> new Font: \(font.fontName) - \(font.pointSize)")
            }
            else if (font.fontName == systemFontBold) {
                font = UIFont(name: customFontBold, size: (font?.pointSize)!)
                //xlog.debug("Old font: \(oldFontName) -> new Font: \(font.fontName) - \(font.pointSize)")
            }
        }
    }
}
0
benjam

Rien de tout cela n'a fonctionné pour moi, mais cela a fonctionné. Vous devez également utiliser la police système dans IB

#import <UIKit/UIKit.h>

@interface UILabelEx : UILabel


@end

#import "UILabelEx.h"
#import "Constants.h"

@implementation UILabelEx

- (void) traitCollectionDidChange: (UITraitCollection *) previousTraitCollection {
    [super traitCollectionDidChange: previousTraitCollection];

    self.font = [UIFont fontWithName:APP_FONT size:self.font.pointSize];   
}
@end
0
Jules