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

104

Xcode 6 a une nouvelle fonctionnalité où les polices et les tailles de police dans UILabel, UITextFieldet UIButtonpeuvent être réglés automatiquement en fonction de la classe de taille de la configuration actuelle de l' appareil, à droite dans le story - board. Par exemple, vous pouvez définir a UILabelpour utiliser la taille de police 12 sur les configurations «toute largeur, hauteur compacte» (comme sur les iPhones en mode 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:

developer.apple.com/size_class

Il s'agit d'une fonctionnalité intéressante en théorie, car elle pourrait rendre inutile la définition de différentes polices par programme sur les fonctionnalités de l'interface utilisateur en fonction de la configuration de l'appareil. À l'heure actuelle, j'ai un 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. J'étais donc très enthousiasmé au départ par cette fonctionnalité, mais j'ai trouvé qu'elle présentait un grave problème d'utilisation réelle pour moi (peut-être un bogue). Notez que je construis avec le SDK 8 et que je fixe un objectif de déploiement minimum d' iOS 8 , donc cela n'a rien à voir avec la compatibilité avec les anciennes versions d'iOS.

Le problème est le suivant: si je définis des tailles de police différentes pour différentes classes de taille et que j'utilise la police "Système" 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 , cela fonctionne également comme prévu. Mais lorsque j'essaie d'utiliser différentes tailles de police personnalisée 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 apparaissent sur lepériphérique et simulateur comme 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'il remplace la police système par celle spécifiée dans le storyboard). Donc, fondamentalement, la fonctionnalité de classe de taille semble être interrompue pour les polices personnalisées. De plus, il est intéressant de noter que les polices personnalisées s'affichent et ajustent correctement la taille dans le volet "Aperçu" XCode 6 pour le contrôleur de vue: cela ne fonctionne que lorsqu'il est exécuté sur le système iOS réel (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 "Système" à la place.

Quoi qu'il en soit, quelqu'un d'autre a-t-il vu ce problème dans Xcode 6 ?

Des idées pour savoir s'il s'agit d'un bogue dans iOS 8, Xcode ou quelque chose du genre

Est-ce que je fais mal?

La seule solution de contournement que j'ai trouvée, comme je l'ai dit, est de continuer à définir les polices par programmation comme je l'ai été pour environ trois 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 System n'est pas acceptable pour notre conception.


INFORMATIONS SUPPLÉMENTAIRES: Depuis Xcode 8.0, le bogue est corrigé.

mnémie
la source
3
Je peux confirmer que c'est toujours le cas dans la version de l'App Store xCode 6.1 (6A1052d).
jodm
3
Ce n'est pas une solution, mais j'ai réussi à contourner le problème en utilisant une catégorie. Ma catégorie UILabel + Font.h (j'en ai aussi une pour les boutons) contient le code suivant. - (void) awakeFromNib {float size = [self.font pointSize]; self.font = [UIFont fontWithName: @ "YourCustomFont" size: size]; } Ainsi, cela nous permet d'utiliser les classes de taille avec la police System pour définir les valeurs de points dans différentes classes de taille, et la catégorie garantit que la police correcte est définie. Peut-être pourriez-vous essayer jusqu'à ce qu'Apple publie une mise à jour?
Daniel Retief Fourie
2
Toujours pas résolu dans Xcode 6.3.1. Je suis en colère contre ça.
Fury
7
Ne fonctionne toujours pas avec Xcode 7 final. Je ne comprends pas pourquoi Apple ne le résout pas.
ernesto
5
ne fonctionne toujours pas sur Xcode 7.2 iOS 9.2
Mojtaba

Réponses:

41

Solution rapide:

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

Inspecteur des attributs d'étiquette

2) Sous-classe UILabel et remplace 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];
    }

}

Au fait, c'est une technique très pratique pour les polices iconiques

rasoir28
la source
Bien que ce soit une bonne solution de contournement si la police utilisée dans toute l'application est la même, c'est un peu moins pratique si vous devez utiliser différentes polices à différents endroits, car vous devez alors avoir différentes sous-classes, etc. Dans mon cas, j'ai déjà avait configuré des polices programmatiques, et il existe de nombreuses façons de le faire. Mais le but de ma question n'est pas de demander comment faire cela par programme: c'est de se demander pourquoi cela ne fonctionne pas correctement avec Storyboards (c'est un bogue Apple).
mnemia
@mnemia Je suis d'accord. Mon objectif est juste de montrer comment gérer ce bug.
razor28
1
Fonctionne très bien. Cependant, vous n'avez pas besoin de sous-classer UILabel, vous pouvez simplement remplacer cette méthode dans la vue qui contient l'étiquette et le faire. De self.label.font = ...cette façon, vous pouvez utiliser différentes polices à différents endroits.
KPM
Ou vous pouvez configurer un @IBInspectable, pour stocker le nom de la police, pour réutiliser la même classe d'étiquettes personnalisée et demander différentes polices directement dans le storyboard.
Craig Grummitt
Existe-t-il une solution similaire pour UITextField?
Chris Byatt
17

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 didSetobservateur, pour éviter les pièges de layoutSubviews()(boucle infinie pour les hauteurs de ligne de vue de 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 l'utiliser éventuellement avec @IBDesignable
  • Malheureusement, @IBDesignablene fonctionne pas avec traitCollectionselon cet autre article de la pile
  • L'instruction de commutation de collection de traits est effectuée sur UIScreen.mainScreen()plutôt que selfpar cet article de pile
Robert Chen
la source
Meilleure réponse, mais malheureusement sous Voté..Merci
Rohit Pradhan
9

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

Phil
la source
1
J'ai pu faire la même chose en modifiant la largeur de mon étiquette dans chaque classe de taille. Bon conseil!
dmzza
1
Je n'ai pas réussi à réduire la police en définissant une hauteur fixe plus petite pour mon UILabel, comment l'avez-vous fait fonctionner exactement? adjustsFontSizeToFitWidthsemble fonctionner uniquement pour la largeur (que je ne peux pas utiliser car le texte des étiquettes est dynamique).
ernesto
J'ai le même problème avec l'ajustement à la largeur
Jules
1
J'ai fini par définir la contrainte de hauteur de l'étiquette proportionnellement à la hauteur de la vue supervisée. Cela fonctionne bien tant que vous définissez le paramètre "Lines" du Label dans IB sur 0.
Dorian Roy
8

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

Pour toute autre personne occupant ce poste, j'aimerais 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 IBInspectablepour vos attributs d'exécution. Cela rendra ces attributs accessibles à partir de l '«Inspecteur d'attributs», visuellement juste au-dessus de la position par défaut des 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:

entrez la description de l'image ici

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 puisse obtenir du comportement prévu de XCode. Espérons qu'une solution appropriée est en route avec iOS 9 cet été.

Joakim
la source
J'ai ajouté selon vos instructions mais je ne suis pas en mesure de trouver visuellement l'accès "Inspecteur d'attributs" dans le xib alors comment y parvenir?
Darshan Kunjadiya
3

Solution similaire à celle de @ razor28 mais je pense qu'un peu plus universelle. En outre, fonctionne très bien sur les anciennes versions iOS

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

Vitaly
la source
Et bien sûr, vous pouvez définir la propriété overrideFontName dans Interface Builder, ce qui permet d'éviter d'écrire du code inutile
Vitaly
Cela n'a pas fonctionné pour moi, j'ai répertorié toutes les polices installées dans la méthode ci-dessus et ma police a été répertoriée et affectée dans la variable mais n'a pas modifié la police affichée dans l'étiquette du simulateur et je suppose que l'appareil
Jules
3

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 écraser 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 à l'aide de la police système

entrez la description de l'image ici

Foti Dim
la source
@ Maarten1909 Faites-vous référence à la solution de contournement ou à la méthode XCode simple? Si le premier pouvez-vous préciser la version afin que j'essaie de la reproduire?
Foti Dim du
Il semble que j'ai toujours utilisé le mauvais nom de famille de polices. Il s'avère que dans ce cas, les polices sont réinitialisées à une police système avec une taille de 14,0 pintes. Ma faute. Mais cela pourrait être utile aux autres!
Berendschot
0

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.

Nirmit Dagly
la source
0

Aucun de ceux-ci 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
Jules
la source
0

Toujours pas de bonne réponse signée. Ce code fonctionne très 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];

    }
}
Max Smirnov
la source
0

Une combinaison de certaines des réponses ultérieures ci-dessus a été utile. Voici comment j'ai résolu le bogue 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)")
            }
        }
    }
}
benjam
la source
-1

J'utilise Swift, XCode 6.4. Alors c'est ce que j'ai fait

import Foundation
import UIKit

    @IBDesignable class ExUILabel: UILabel {

        @IBInspectable var fontName: String = "Default-Font" {
            didSet {
                self.font = UIFont(name: fontName, size:self.font.pointSize)
            }
        }

        override func layoutSubviews() {
            super.layoutSubviews()
            self.font = UIFont(name: fontName, size:self.font.pointSize)
        }
    }
  1. Goto Designer -> Identity Inspector -> Définissez la classe sur ExUILabel

  2. Accédez ensuite à l'inspecteur d'attributs dans le concepteur et définissez le nom de la police.

kakopappa
la source
-1

J'ai trouvé une solution encore plus rapide (je suppose que vous utilisez toujours votre police personnalisée).

Créez une catégorie pour UILabel et incluez-la dans les fichiers en utilisant un storyboard buggy avec des classes de taille et des paramètres de police dédiés pour différentes classes:

@implementation UILabel (FontFixForSizeClassesAppleBug)

- (void)layoutSubviews
{
    [super layoutSubviews];
    if([[UIFont systemFontOfSize:10].familyName isEqualToString:self.font.familyName]) {
        //workaround for interface builder size classes bug which ignores custom font if various classes defined for label: http://stackoverflow.com/questions/26166737/custom-font-sizing-in-xcode6-size-classes-not-working-properly-w-custom-fonts
        self.font = [UIFont fontWithName:@"YOUR_CUSTOM_FONT_NAME" size:self.font.pointSize];
    }
}

@end

Utilisez simplement vos polices personnalisées dans le storyboard. Lorsque l'interpréteur bogué utilisera la police système à la place de la vôtre, cette catégorie la basculera vers votre police personnalisée.

Heps
la source
-1: Ce n'est pas une bonne idée de remplacer les méthodes dans une catégorie comme celle-ci. Raison: stackoverflow.com/questions/5272451/… Au lieu de cela, devrait swizzle (bien que hacky) ou sous-classe.
JRG-Developer
-6

J'ai eu le même problème et j'en ai trouvé un pas vraiment clair, mais une bonne solution!

  1. Commencez par définir la taille de police requise dans le storyboard avec le nom de la police système.
  2. Ensuite, à cette étiquette, vous attribuez une étiquette de 100 à 110 (ou plus, mais 10 me suffisait toujours dans un contrôleur de vue).
  3. Ensuite, mettez ce code dans le fichier source de votre VC et n'oubliez pas de changer le nom de la police. Code en rapide.
override func viewDidLayoutSubviews() {
    for i in 100...110 {
        if let label = view.viewWithTag(i) as? UILabel {
            label.font = UIFont(name: "RotondaC-Bold", size: label.font.pointSize)
        }
    }
}
serg_ov
la source
1
N'utilisez pas de balises. Utilisez IBOutlets. Les balises sont mauvaises. Voir WWDC 2015 session 231: «Si vous utilisez l'API View With Tag ou Set Tag UIView et le code d'expédition, je vais vous encourager à vous en éloigner. Pour remplacer cela, déclarez les propriétés de vos classes, et vous aurez alors de véritables connexions à ces vues dont vous aurez besoin plus tard. »
KPM
je suis triste que cette solution ne soit pas parfaite, mais cela fonctionne!
serg_ov
-8
  • ajoutez les polices fournies par l'application dans votre application .plist
  • ajoutez votre fichier .ttf à l'élément 0
  • utilisez-le [UIFont fontWithName: "example.ttf" size: 10]
user2612184
la source
Ce n'est pas une réponse à ce problème. Évidemment, vous pouvez utiliser des polices personnalisées par programme, mais ma question se réfère à un bogue dans iOS 8 qui empêche les polices personnalisées de changer automatiquement en fonction de la classe de taille.
mnemia