O tamanho de fonte personalizado nas classes de tamanho do Xcode 6 não funciona corretamente com fontes personalizadas

104

Xcode 6 tem um novo recurso onde fontes e tamanhos de fonte em UILabel,UITextField e UIButtonpode ser definido automaticamente com base na classe de tamanho da configuração do dispositivo atual, mesmo no storyboard. Por exemplo, você pode definir a UILabelpara usar o tamanho de fonte 12 em configurações de "qualquer largura, altura compacta" (como em iPhones em paisagem) e o tamanho 18 em configurações de "largura regular, altura regular" (como em iPads ). Mais informações estão disponíveis aqui:

developer.apple.com/size_class

Este é um ótimo recurso em teoria porque pode tornar desnecessário definir fontes diferentes programaticamente em recursos de IU com base na configuração do dispositivo. No momento, tenho um código condicional que define as fontes com base no tipo de dispositivo, mas, obviamente, isso significa que tenho que definir as fontes de forma programática em todo o aplicativo. Eu estava inicialmente muito animado com esse recurso, mas descobri que ele tem um grave problema de uso real para mim (talvez um bug). Observe que estou construindo com base no SDK 8 e definindo um destino mínimo de implantação do iOS 8 , portanto, isso não tem nada a ver com a compatibilidade com versões antigas do iOS.

O problema é o seguinte: Se eu definir tamanhos de fonte diferentes para classes de tamanho diferentes e usar a fonte "Sistema" fornecida pelo iOS , tudo funcionará conforme o esperado e os tamanhos de fonte mudam com base na classe de tamanho. Se eu usar uma fonte personalizada fornecida pelo meu aplicativo (sim, eu a configurei corretamente no pacote do meu aplicativo, pois funciona de forma programática) e definir a fonte personalizada para um rótulo em um storyboard XCode 6 , que também funciona conforme o esperado. Mas quando tento usar tamanhos diferentes de fonte personalizada para classes de tamanhos diferentes, no storyboard, de repente não funciona. A única diferença na configuração é a fonte que escolhi (uma fonte personalizada versus a fonte do sistema). Em vez disso, todas as fontes aparecem nodispositivo e simulador como a fonte padrão do sistema no tamanho padrão , independentemente da classe de tamanho (e eu verifiquei por meio do depurador que ele está substituindo a fonte do sistema pela real especificada no storyboard). Então, basicamente, o recurso de classe de tamanho parece estar quebrado para fontes personalizadas. Além disso, curiosamente, as fontes personalizadas realmente exibem e ajustam o tamanho corretamente no painel "Visualização" do XCode 6 para o controlador de visualização: ele para de funcionar apenas quando executado no sistema iOS real (o que me faz pensar que estou configurando corretamente) .

Tentei várias fontes personalizadas diferentes e não parece funcionar com nenhuma delas, mas sempre funciona se eu usar "Sistema".

Enfim, mais alguém viu esse problema no Xcode 6 ?

Alguma ideia se isso é um bug no iOS 8, Xcode ou algo assim

Eu estou fazendo errado?

A única solução que encontrei, como disse, é continuar a definir as fontes de maneira programática, como fiz em cerca de três versões do iOS, porque isso funciona.

Mas eu adoraria poder usar esse recurso se pudesse fazê-lo funcionar com fontes personalizadas. Usar a fonte System não é aceitável para nosso design.


INFORMAÇÕES ADICIONAIS: a partir do Xcode 8.0, o bug foi corrigido.

mnemia
fonte
3
Posso confirmar que este ainda é o caso no lançamento da app store do xCode 6.1 (6A1052d).
jodm
3
Não é uma solução, mas consegui contornar o problema usando uma categoria. Minha categoria UILabel + Font.h (também tenho uma para botões) contém o código a seguir. - (vazio) awakeFromNib {float size = [self.font pointSize]; self.font = [UIFont fontWithName: @ "YourCustomFont" size: size]; } Portanto, isso nos permite usar as classes de tamanho com a fonte System para definir os valores dos pontos em diferentes classes de tamanho, e a categoria garante que a fonte correta seja definida. Talvez você possa dar uma chance até a Apple lançar uma atualização?
Daniel Retief Fourie
2
Ainda não resolvido no Xcode 6.3.1. Estou com raiva disso.
Fúria de
7
Ainda não funciona com o Xcode 7 final. Não entendo por que a Apple não conserta isso.
ernesto,
5
ainda não está funcionando no Xcode 7.2 iOS 9.2
Mojtaba

Respostas:

41

Correção rápida:

1) Defina as fontes como sistema para classes de tamanho

Inspetor de atributos de etiqueta

2) Subclasse UILabel e substitua o método "layoutSubviews" como:

- (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 propósito, é uma técnica muito conveniente para fontes icônicas

razor28
fonte
Embora esta seja uma boa solução alternativa se a fonte usada em todo o aplicativo for a mesma, é um pouco menos conveniente se você precisar usar fontes diferentes em lugares diferentes, porque então você precisa ter subclasses diferentes, etc. No meu caso, eu já configurou fontes programáticas e há muitas maneiras diferentes de fazer isso. Mas o ponto da minha pergunta não é perguntar como fazer isso programaticamente: é perguntar por que isso não funciona corretamente usando Storyboards (é um bug da Apple).
mnemia de
@mnemia eu concordo. Meu objetivo é apenas mostrar uma maneira de lidar com esse bug.
razor28
1
Funciona bem. Você não precisa criar uma subclasse de UILabel, você pode apenas substituir esse método na visão que contém o rótulo e fazer. self.label.font = ...Desta forma, você pode usar fontes diferentes em lugares diferentes.
KPM
Ou você pode configurar um @IBInspectable para armazenar o nome da fonte, para reutilizar a mesma classe de rótulo personalizado e solicitar fontes diferentes diretamente no storyboard.
Craig Grummitt
Existe uma solução semelhante para UITextField?
Chris Byatt,
17

Depois de tentar de tudo, acabei optando por uma combinação das soluções acima. Usando o 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 permite definir o tamanho da fonte no Storyboard
  • Ele usa um didSetobservador, para evitar as armadilhas de layoutSubviews()(loop infinito para alturas de linha de visualização de tabela dinâmica) e awakeFromNib()(veja o comentário de @cocoaNoob)
  • Ele usa classes de tamanho em vez do idioma do dispositivo, na esperança de eventualmente usar isso com @IBDesignable
  • Infelizmente, @IBDesignablenão funciona de traitCollectionacordo com este outro artigo de pilha
  • A instrução de troca de coleção de traços é realizada em UIScreen.mainScreen()vez de selfpor este artigo de pilha
Robert Chen
fonte
Melhor resposta, mas tristemente votado
Rohit Pradhan
9

Solução alternativa para UILabel: mantenha o mesmo tamanho de fonte em todas as classes de tamanho, mas altere a altura da etiqueta de acordo com cada classe de tamanho. Seu rótulo deve ter autoshrink ativado. Funcionou bem no meu caso.

Phil
fonte
1
Consegui fazer a mesma coisa mudando a largura da minha etiqueta em cada classe de tamanho. Boa dica!
dmzza
1
Eu não fiz a fonte encolher definindo uma altura fixa menor para meu UILabel, como exatamente você fez isso funcionar? adjustsFontSizeToFitWidthparece funcionar apenas para largura (que não posso usar porque o texto dos rótulos é dinâmico).
ernesto,
Tenho o mesmo problema com o ajuste para caber na largura
Jules
1
Acabei definindo a restrição de altura do Label em proporção à altura do superview. Isso funciona bem, contanto que você defina o parâmetro "Linhas" do rótulo em IB como 0.
Dorian Roy
8

Este bug (e outros Xcode-Size Classes relacionados) me causou sérios problemas recentemente, pois eu tive que passar por um enorme arquivo de storyboard invadindo coisas.

Para qualquer outra pessoa nesta posição, eu gostaria de acrescentar algo na resposta de @ razor28 para aliviar a dor.

No arquivo de cabeçalho de sua subclasse customizada, use IBInspectablepara seus atributos de tempo de execução. Isso tornará esses atributos acessíveis no "Attributes Inspector", visualmente logo acima da posição padrão para as configurações de fonte.

Exemplo de uso:

@interface MyCustomLabel : UILabel

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

@end

Isso produzirá muito útil esta saída:

insira a descrição da imagem aqui

Um benefício adicional é que agora não precisamos adicionar os atributos de tempo de execução manualmente para cada rótulo. Este é o mais próximo que eu pude chegar do comportamento pretendido do XCode. Esperançosamente, uma correção adequada está a caminho com o iOS 9 neste verão.

joakim
fonte
Eu adicionei de acordo com suas instruções, mas não consigo encontrar o acesso "Inspetor de atributos" visualmente no xib. Como fazê-lo?
Darshan Kunjadiya,
3

Solução semelhante à de @ razor28, mas acho um pouco mais universal. Além disso, funciona bem em versões mais antigas do iOS

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

Vitaly
fonte
E claro, você pode definir a propriedade overrideFontName no Interface Builder, o que permite evitar a escrita de código desnecessário
Vitaly
Isso não funcionou para mim, eu listei todas as fontes instaladas dentro do método acima e minha fonte foi listada e atribuída na variável, mas não alterei a fonte exibida no rótulo do simulador e presumo que o dispositivo
Jules
3

O bug ainda é válido no XCode 7.0 GM.

A solução do Razor28 causa loops infinitos em alguns casos. Minha experiência tem sido usá-lo em conjunto com o SwipeView .

Em vez disso, sugiro que você:

1) Subclasse UILabel e substitua setFont:

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

2) Defina a classe personalizada de seus UILabels e, em seguida, defina as classes de tamanho da fonte usando a fonte do sistema

insira a descrição da imagem aqui

Foti Dim
fonte
@ Maarten1909 Você está se referindo à solução alternativa ou ao método direto do XCode? Se for o primeiro, você pode especificar a versão para que eu tente reproduzi-la?
Foti Dim
Parece que tenho usado o nome da família da fonte errado o tempo todo. Acontece que, nesse caso, as fontes são redefinidas para uma fonte do sistema com 14.0 pint-size. Foi mal. Mas pode ser útil para outras pessoas!
Berendschot
0

O problema ainda é que você não pode usar o recurso para definir fontes para classes de tamanho diferentes do construtor de interface.

Basta definir a fonte com base no dispositivo que você deseja, como a seguir:

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

Isso funciona perfeitamente em todos os dispositivos.

Nirmit Dagly
fonte
0

Nada disso funcionou para mim, mas isso funcionou. Você também precisa usar a fonte do sistema em 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
fonte
0

Ainda sem resposta certa assinada. Este código funciona bem para mim. Você deve desativar o tamanho da fonte para as classes de tamanho no construtor de interface primeiro. No IB você pode usar uma fonte personalizada.

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

Uma combinação de algumas das respostas posteriores acima foi útil. Veja como resolvi o bug do IB por meio de uma extensão 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
fonte
-1

Estou usando Swift, XCode 6.4. Então foi isso que eu fiz

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. Vá para Designer -> Inspetor de identidade -> Defina a classe como ExUILabel

  2. Em seguida, vá para o inspetor de atributos no designer e defina o nome da fonte.

kakopappa
fonte
-1

Eu vim com uma correção ainda mais rápida (presumo que você sempre use sua única fonte personalizada).

Crie uma categoria para UILabel e inclua em arquivos usando storyboard com bugs com classes de tamanho e configuração de fonte dedicada para várias 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

Basta usar suas fontes personalizadas no storyboard. Quando o intérprete com erros usará a fonte do sistema em vez da sua própria, esta categoria a mudará para sua fonte personalizada.

Heps
fonte
-1: Não é uma boa ideia substituir métodos em uma categoria como esta. Motivo: stackoverflow.com/questions/5272451/… Em vez disso, deve swizzle (embora hacky) ou subclasse.
Desenvolvedor JRG,
-6

Eu tive o mesmo problema e encontrei uma solução não muito clara, mas boa!

  1. Primeiro, você define o tamanho da fonte necessária no storyboard com o nome da fonte do sistema.
  2. Então, para este rótulo, você atribui uma tag de 100 a 110 (ou mais, mas 10 sempre foi o suficiente para mim em um controlador de visualização).
  3. Em seguida, coloque este código no arquivo-fonte do seu VC e não se esqueça de alterar o nome da fonte. Codifique rapidamente.
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
fonte
1
Não use tags. Use IBOutlets. Tags são ruins. Consulte a sessão 231 do WWDC 2015: “Se você estiver usando a API UIView View With Tag ou Set Tag e o código de remessa, vou incentivá-lo a se afastar disso. Como um substituto para isso, declare propriedades em suas classes, e então você terá conexões reais com as visualizações que você precisa mais tarde. ”
KPM
lamento que esta solução não seja perfeita, mas funciona!
serg_ov
-8
  • adicione fontes fornecidas pelo aplicativo em seu aplicativo .plist
  • adicione seu arquivo .ttf ao item 0
  • use-o [UIFont fontWithName: "example.ttf" size: 10]
user2612184
fonte
Esta não é uma resposta para este problema. Obviamente, você pode usar fontes personalizadas de forma programática, mas minha pergunta se refere a um bug no iOS 8 que impede que fontes personalizadas mudem automaticamente dependendo da classe de tamanho.
mnemia de