Il dimensionamento personalizzato dei caratteri nelle classi di dimensioni Xcode 6 non funziona correttamente con i caratteri personalizzati


104

Xcode 6 presenta una nuova caratteristica in cui caratteri e dimensioni dei caratteri a UILabel, UITextFielde UIButtonpossono essere impostate automaticamente in base alla classe dimensione della configurazione del dispositivo corrente, nel storyboard. Ad esempio, puoi impostare a UILabelper utilizzare la dimensione del carattere 12 su "qualsiasi larghezza, altezza compatta" (come sugli iPhone in orizzontale) e la dimensione 18 su configurazioni "larghezza regolare, altezza regolare" (come sugli iPad ). Ulteriori informazioni sono disponibili qui:

developer.apple.com/size_class

Questa è una grande funzionalità in teoria perché potrebbe rendere non necessario impostare a livello di codice diversi caratteri sulle funzionalità dell'interfaccia utente in base alla configurazione del dispositivo. In questo momento, ho un codice condizionale che imposta i caratteri in base al tipo di dispositivo, ma ovviamente ciò significa che devo impostare i caratteri a livello di codice ovunque nell'app. Quindi inizialmente ero davvero entusiasta di questa funzione, ma ho scoperto che ha un grave problema di utilizzo effettivo per me (forse un bug). Tieni presente che sto compilando l'SDK 8 e sto impostando un obiettivo di distribuzione minimo di iOS 8 , quindi questo non ha nulla a che fare con la compatibilità con le vecchie versioni di iOS.

Il problema è questo: se imposto caratteri di dimensioni diverse per classi di dimensioni diverse e utilizzo il carattere "Sistema" fornito da iOS , tutto funziona come previsto e le dimensioni dei caratteri cambiano in base alla classe di dimensioni. Se utilizzo un carattere personalizzato fornito dalla mia applicazione (sì, l'ho impostato correttamente nel pacchetto dell'applicazione, poiché funziona a livello di programmazione) e imposto il carattere personalizzato su un'etichetta in uno storyboard XCode 6 , anche questo funziona come previsto. Ma quando provo a utilizzare dimensioni diverse del carattere personalizzato per classi di dimensioni diverse, nello storyboard, improvvisamente non funziona. L'unica differenza nella configurazione è il carattere che ho scelto (uno personalizzato rispetto al carattere di sistema). Invece, tutti i caratteri vengono visualizzati nel filedispositivo e simulatore come font di sistema predefinito alla dimensione predefinita , indipendentemente dalla classe di dimensione (e ho verificato tramite il debugger che sta sostituendo il font di sistema con quello effettivo specificato nello storyboard). Quindi, in pratica, la funzione della classe di dimensione sembra essere interrotta per i caratteri personalizzati. Inoltre, è interessante notare che i caratteri personalizzati effettivamente visualizzano e regolano correttamente le dimensioni nel riquadro "Anteprima" di XCode 6 per il controller di visualizzazione: smette di funzionare solo quando viene eseguito sul sistema iOS effettivo (il che mi fa pensare che lo sto configurando correttamente) .

Ho provato più caratteri personalizzati diversi, e non sembra funzionare per nessuno di essi, ma funziona sempre se uso invece "Sistema".

Ad ogni modo, qualcun altro ha visto questo problema in Xcode 6 ?

Qualche idea sul fatto che si tratti di un bug in iOS 8, Xcode o qualcosa del genere

Sto sbagliando?

L'unica soluzione alternativa che ho trovato, come ho detto, è continuare a impostare i caratteri a livello di codice come sono stato per circa tre versioni di iOS perché funziona.

Ma mi piacerebbe poter usare questa funzione se potessi farlo funzionare con caratteri personalizzati. L'uso del carattere di sistema non è accettabile per il nostro design.


INFORMAZIONI AGGIUNTIVE: A partire da Xcode 8.0, il bug è stato risolto.


3
Posso confermare che questo è ancora il caso della versione dell'app store di xCode 6.1 (6A1052d).
jodm

3
Non è una soluzione, ma sono riuscito a aggirare il problema utilizzando una categoria. La mia categoria UILabel + Font.h (ne ho uno anche per i pulsanti) contiene il seguente codice. - (void) awakeFromNib {float size = [self.font pointSize]; self.font = [UIFont fontWithName: @ "YourCustomFont" size: size]; } Quindi, questo ci consente di utilizzare le classi di dimensione con il carattere di sistema per impostare i valori in punti in diverse classi di dimensione e la categoria garantisce che sia impostato il carattere corretto. Forse potresti provare fino a quando Apple non rilascerà un aggiornamento?
Daniel Retief Fourie

2
Ancora non risolto in Xcode 6.3.1. Sono arrabbiato per questo.
Fury

7
Ancora non funziona con Xcode 7 final. Non capisco perché Apple non lo risolva.
ernesto

5
ancora non funziona su Xcode 7.2 iOS 9.2
Mojtaba

Risposte:


41

Soluzione rapida:

1) Imposta i caratteri come Sistema per le classi di dimensione

Etichetta attributi inspector

2) Sottoclasse UILabel e sovrascrivi il metodo "layoutSubviews" come:

- (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];
    }

}

A proposito, è una tecnica molto conveniente per i caratteri iconici


Sebbene questa sia una buona soluzione alternativa se il carattere utilizzato in tutta l'app è lo stesso, è un po 'meno conveniente se devi usare caratteri diversi in posti diversi, perché allora devi avere sottoclassi diverse, ecc. Nel mio caso, ho già aveva impostato i caratteri programmatici e ci sono molti modi diversi per farlo. Ma il punto della mia domanda non è chiedere come farlo in modo programmatico: è chiedere perché questo non funziona correttamente usando Storyboard (è un bug di Apple).
mnemia

@mnemia sono d'accordo. Il mio scopo è solo quello di mostrare un modo come gestire questo bug.
razor28

1
Funziona alla grande. Non è necessario creare una sottoclasse UILabel, puoi semplicemente sovrascrivere questo metodo nella vista che contiene l'etichetta e fare In self.label.font = ...questo modo puoi usare caratteri diversi in posti diversi.
KPM

Oppure puoi impostare un @IBInspectable, per memorizzare il nome del carattere, per riutilizzare la stessa classe di etichetta personalizzata e richiedere caratteri diversi direttamente nello storyboard.
Craig Grummitt

Esiste una soluzione simile per UITextField?
Chris Byatt

17

Dopo aver provato tutto, alla fine ho optato per una combinazione delle soluzioni di cui sopra. Utilizzando 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 ti consente di impostare la dimensione del carattere nello Storyboard
  • Utilizza un didSetosservatore, per evitare le insidie ​​da layoutSubviews()(ciclo infinito per le altezze delle righe della vista tabella dinamica) e awakeFromNib()(vedi il commento di @ cocoaNoob)
  • Utilizza classi di dimensioni piuttosto che l'idioma del dispositivo, nella speranza di poterlo utilizzare eventualmente con @IBDesignable
  • Purtroppo, @IBDesignablenon funziona traitCollectionsecondo questo altro articolo dello stack
  • L'istruzione switch di raccolta dei tratti viene eseguita UIScreen.mainScreen()anziché in base selfa questo articolo dello stack

Migliore risposta, ma purtroppo sotto Votato .. Grazie
Rohit Pradhan

9

Soluzione alternativa per UILabel: mantenere la stessa dimensione del carattere su tutte le classi di dimensioni, ma modificare l'altezza dell'etichetta di conseguenza in ciascuna classe di dimensioni. La tua etichetta deve avere il restringimento automatico abilitato. Ha funzionato bene nel mio caso.


1
Sono stato in grado di fare la stessa cosa modificando la larghezza dell'etichetta in ciascuna classe di dimensioni. Bel consiglio!
dmzza

1
Non ho ottenuto che il carattere si restringesse impostando un'altezza fissa più piccola per la mia UILabel, come hai fatto esattamente a farlo funzionare? adjustsFontSizeToFitWidthsembra funzionare solo per la larghezza (che non posso usare perché il testo delle etichette è dinamico).
ernesto

Ho lo stesso problema con l'adattamento alla larghezza
Jules

1
Ho finito per impostare il vincolo di altezza dell'etichetta in proporzione all'altezza della superview. Funziona bene fintanto che imposti il ​​parametro "Linee" dell'etichetta in IB su 0.
Dorian Roy

8

Questo bug (e altri relativi a Xcode - Classi di dimensione) mi ha causato un serio dolore di recente poiché ho dovuto passare attraverso un enorme file di storyboard che hackerava le cose.

Per chiunque altro in questa posizione, vorrei aggiungere qualcosa in cima alla risposta di @ razor28 per alleviare il dolore.

Nel file di intestazione della tua sottoclasse personalizzata, utilizza IBInspectableper gli attributi di runtime. Questo renderà questi attributi accessibili da "Attributes Inspector", visivamente proprio sopra la posizione predefinita per le impostazioni dei caratteri.

Esempio di utilizzo:

@interface MyCustomLabel : UILabel

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

@end

Questo produrrà molto utile questo output:

inserisci qui la descrizione dell'immagine

Un ulteriore vantaggio è che ora non è necessario aggiungere manualmente gli attributi di runtime per ciascuna etichetta. Questo è quanto di più vicino potrei ottenere al comportamento previsto di XCode. Si spera che questa estate sia in arrivo una soluzione adeguata con iOS 9.


Ho aggiunto secondo le tue istruzioni ma non riesco a trovare l'accesso "Attributes Inspector" visivamente in xib, quindi come ottenerlo?
Darshan Kunjadiya

3

Soluzione simile a quella di @ razor28 ma penso un po 'più universale. Inoltre, funziona bene con le versioni iOS precedenti

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


E certo, puoi impostare la proprietà overrideFontName in Interface Builder che consente di evitare di scrivere codice non necessario
Vitaly

Questo non ha funzionato per me, ho elencato tutti i caratteri installati all'interno del metodo sopra e il mio carattere è stato elencato e assegnato nella variabile ma non ha alterato la visualizzazione dei caratteri nell'etichetta sul simulatore e presumo che il dispositivo
Jules

3

Il bug è ancora valido in XCode 7.0 GM.

La soluzione di Razor28 causa loop infiniti in alcuni casi. La mia esperienza è stata con l'utilizzo in combinazione con SwipeView .

Invece, ti suggerisco di:

1) Sottoclasse UILabel e sovrascrivi setFont:

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

2) Imposta la classe personalizzata delle tue UILabels e quindi imposta le classi di dimensione del carattere utilizzando il carattere di sistema

inserisci qui la descrizione dell'immagine


@ Maarten1909 Ti riferisci alla soluzione alternativa o al modo semplice con XCode? Se il primo puoi specificare la versione così provo a riprodurlo?
Foti Dim

Sembra che abbia sempre usato il nome della famiglia di caratteri sbagliato. Si scopre che in tal caso i caratteri vengono reimpostati su un carattere di sistema con una dimensione di 14.0 pint. Colpa mia. Ma potrebbe essere utile ad altri!
Berendschot

0

Il problema è ancora lì che non è possibile utilizzare la funzione per impostare i caratteri per classi di dimensioni diverse dal generatore di interfacce.

Basta impostare il carattere in base al dispositivo che desideri proprio come di seguito:

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]]; 
}

Funziona perfettamente su tutti i dispositivi.


0

Nessuno di questi ha funzionato per me, ma questo ha funzionato. È inoltre necessario utilizzare il carattere di sistema in 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

Ancora nessuna risposta giusta firmata. Questo codice funziona bene per me. È necessario disattivare prima la dimensione del carattere per le classi di dimensione nel generatore di interfacce. In IB puoi usare caratteri personalizzati.

- (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

Una combinazione di alcune delle risposte successive di cui sopra è stata utile. Ecco come ho risolto il bug IB tramite un'estensione UILabel Swift:

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)")
            }
        }
    }
}

-1

Sto usando Swift, XCode 6.4. Quindi questo è quello che ho fatto

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 -> Imposta la classe su ExUILabel

  2. Quindi vai su Attribute inspector nel designer e imposta il nome del carattere.


-1

Ho trovato una soluzione ancora più veloce (presumo che tu usi sempre il tuo unico carattere personalizzato).

Crea una categoria per UILabel e includila nei file utilizzando uno storyboard con bug con classi di dimensioni e impostazione dei caratteri dedicata per varie classi:

@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

Usa i tuoi caratteri personalizzati nello storyboard. Quando l'interprete con bug utilizzerà il carattere di sistema invece del tuo, questa categoria lo passerà al tuo carattere personalizzato.


-1: non è una buona idea sovrascrivere i metodi in una categoria come questa. Motivo: stackoverflow.com/questions/5272451/… Invece, dovrebbe swizzle (anche se hacky) o sottoclasse.
JRG-Developer

-6

Ho avuto lo stesso problema e ho trovato una soluzione non proprio chiara, ma ottima!

  1. Per prima cosa imposti la dimensione del carattere richiesta nello storyboard con il nome del carattere di sistema.
  2. Quindi a questa etichetta si assegna un tag da 100 a 110 (o più, ma 10 mi è sempre stato sufficiente in un controller di visualizzazione).
  3. Quindi inserisci questo codice nel file sorgente del tuo VC e non dimenticare di cambiare il nome del carattere. Codice in rapido.
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)
        }
    }
}

1
Non utilizzare tag. Usa IBOutlets. I tag sono cattivi. Vedere la sessione 231 del WWDC 2015: "Se utilizzi l'API UIView View With Tag o Set Tag UIView e il codice di spedizione, ti incoraggio ad allontanarti da questo. In sostituzione di questo, dichiara le proprietà sulle tue classi, e quindi avrai connessioni reali a quelle viste di cui avrai bisogno in seguito. "
KPM

mi dispiace che questa soluzione non sia perfetta, ma funziona!
serg_ov

-8
  • aggiungi i caratteri forniti dall'applicazione nella tua app .plist
  • aggiungi il tuo file .ttf all'elemento 0
  • usalo [UIFont fontWithName: "example.ttf" size: 10]

Questa non è una risposta a questo problema. Ovviamente puoi usare i caratteri personalizzati a livello di programmazione, ma ciò a cui si riferisce la mia domanda è un bug in iOS 8 che impedisce ai caratteri personalizzati di cambiare automaticamente a seconda della classe di dimensione.
mnemia
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.