El tamaño de fuente personalizado en las clases de tamaño de Xcode 6 no funciona correctamente con fuentes personalizadas

104

Xcode 6 tiene una nueva característica donde fuentes y tamaños de fuente en UILabel, UITextFieldy UIButtonse pueden ajustar automáticamente en función de la clase de tamaño de la configuración actual del dispositivo, justo en el guión. Por ejemplo, puede configurar a UILabelpara usar el tamaño de fuente 12 en configuraciones de "cualquier ancho, altura compacta" (como en iPhones en horizontal) y el tamaño 18 en configuraciones de "ancho normal, altura normal" (como en iPads ). Más información está disponible aquí:

developer.apple.com/size_class

En teoría, esta es una gran característica porque podría hacer que no sea necesario configurar mediante programación diferentes fuentes en las características de la interfaz de usuario según la configuración del dispositivo. En este momento, tengo un código condicional que establece las fuentes según el tipo de dispositivo, pero obviamente, eso significa que tengo que configurar las fuentes mediante programación en todas partes de la aplicación. Así que inicialmente estaba muy emocionado con esta función, pero descubrí que tiene un problema severo de uso real para mí (tal vez un error). Tenga en cuenta que estoy construyendo contra SDK 8 y estableciendo un objetivo de implementación mínimo de iOS 8 , por lo que esto no tiene nada que ver con la compatibilidad con versiones anteriores de iOS.

El problema es el siguiente: si establezco diferentes tamaños de fuente para diferentes clases de tamaño y uso la fuente "Sistema" proporcionada por iOS , todo funciona como se esperaba y los tamaños de fuente cambian según la clase de tamaño. Si uso una fuente personalizada proporcionada por mi aplicación (sí, la he configurado correctamente en mi paquete de aplicaciones, ya que funciona mediante programación) y configuro la fuente personalizada en una etiqueta en un guión gráfico de XCode 6 , eso también funciona como se esperaba. Pero cuando trato de usar diferentes tamaños de la fuente personalizada para diferentes clases de tamaño, en el guión gráfico, de repente no funciona. La única diferencia en la configuración es la fuente que he elegido (una personalizada frente a la fuente del sistema). En cambio, todas las fuentes aparecen en ladispositivo y simulador como la fuente predeterminada del sistema en el tamaño predeterminado , independientemente de la clase de tamaño (y verifiqué a través del depurador que está sustituyendo la fuente del sistema por la real especificada en el guión gráfico). Básicamente, la característica de clase de tamaño parece estar rota para fuentes personalizadas. Además, curiosamente, las fuentes personalizadas realmente se muestran y ajustan el tamaño correctamente en el panel "Vista previa" de XCode 6 para el controlador de vista: deja de funcionar solo cuando se ejecuta en el sistema iOS real (lo que me hace pensar que lo estoy configurando correctamente) .

Probé varias fuentes personalizadas diferentes, y no parece funcionar para ninguna de ellas, pero siempre funciona si uso "Sistema" en su lugar.

De todos modos, ¿alguien más ha visto este problema en Xcode 6 ?

Cualquier idea sobre si esto es un error en iOS 8, Xcode o algo

Estoy haciendo mal

La única solución que he encontrado, como dije, es continuar configurando las fuentes mediante programación como lo he hecho para aproximadamente tres versiones de iOS porque eso funciona.

Pero me encantaría poder utilizar esta función si pudiera hacer que funcione con fuentes personalizadas. El uso de la fuente System no es aceptable para nuestro diseño.


INFORMACIÓN ADICIONAL: A partir de Xcode 8.0, el error está corregido.

mnemia
fuente
3
Puedo confirmar que este sigue siendo el caso en la versión de la tienda de aplicaciones xCode 6.1 (6A1052d).
jodm
3
No es una solución, pero me las arreglé para solucionar el problema usando una categoría. Mi categoría UILabel + Font.h (también tengo uno para botones) contiene el siguiente código. - (void) awakeFromNib {tamaño flotante = [self.font pointSize]; self.font = [UIFont fontWithName: @ "YourCustomFont" tamaño: tamaño]; } Entonces, esto nos permite usar las clases de tamaño con la fuente del sistema para establecer los valores de puntos en diferentes clases de tamaño, y la categoría asegura que se configure la fuente correcta. ¿Quizás podrías intentarlo hasta que Apple lance una actualización?
Daniel Retief Fourie
2
Aún no se resuelve en Xcode 6.3.1. Estoy enojado por esto.
Fury
7
Todavía no funciona con Xcode 7 final. No entiendo por qué Apple no lo soluciona.
ernesto
5
todavía no funciona en Xcode 7.2 iOS 9.2
Mojtaba

Respuestas:

41

Arreglo rápido:

1) Establecer fuentes como Sistema para clases de tamaño

Inspector de atributos de etiqueta

2) Subclase UILabel y anule el 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];
    }

}

Por cierto, es una técnica muy conveniente para fuentes icónicas.

maquinilla de afeitar28
fuente
Si bien esta es una buena solución si la fuente utilizada en toda la aplicación es la misma, es un poco menos conveniente si necesita usar diferentes fuentes en diferentes lugares, porque entonces debe tener diferentes subclases, etc. En mi caso, ya había configurado fuentes programáticas, y hay muchas formas diferentes de hacerlo. Pero el punto de mi pregunta no es preguntar cómo hacer esto programáticamente: es preguntar por qué esto no funciona correctamente usando Storyboards (es un error de Apple).
mnemia
@mnemia estoy de acuerdo. Mi objetivo es solo mostrar una forma de manejar ese error.
razor28
1
Funciona genial. Sin embargo, no necesita crear una subclase de UILabel, puede simplemente anular este método en la vista que contiene la etiqueta y hacerlo. De self.label.font = ...esta manera, puede usar diferentes fuentes en diferentes lugares.
KPM
O puede configurar un @IBInspectable para almacenar el nombre de la fuente, reutilizar la misma clase de etiqueta personalizada y solicitar diferentes fuentes directamente en el guión gráfico.
Craig Grummitt
¿Existe una solución similar para UITextField?
Chris Byatt
17

Después de probar todo, finalmente me decidí por una combinación de las soluciones anteriores. Usando 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 le permite establecer el tamaño de fuente en el Storyboard
  • Utiliza un didSetobservador, para evitar los errores de layoutSubviews()(bucle infinito para alturas de fila de vista de tabla dinámica) y awakeFromNib()(ver el comentario de @ cocoaNoob)
  • Utiliza clases de tamaño en lugar del idioma del dispositivo, con la esperanza de usarlo eventualmente con @IBDesignable
  • Lamentablemente, @IBDesignableno funciona de traitCollectionacuerdo con este otro artículo de pila
  • La declaración de cambio de colección de rasgos se realiza en UIScreen.mainScreen()lugar de selfsegún este artículo de pila
Robert Chen
fuente
Mejor respuesta, pero lamentablemente bajo Voted..Thanks
Rohit Pradhan
9

Solución alternativa para UILabel: mantenga el mismo tamaño de fuente en todas las clases de tamaño, pero cambie la altura de la etiqueta en consecuencia en cada clase de tamaño. Su etiqueta debe tener habilitado el encogimiento automático. Funcionó muy bien en mi caso.

Phil
fuente
1
Pude hacer lo mismo cambiando el ancho de mi etiqueta en cada clase de tamaño. ¡Buen consejo!
dmzza
1
No logré que la fuente se encogiera al establecer una altura fija más pequeña para mi UILabel, ¿cómo exactamente lo hizo funcionar? adjustsFontSizeToFitWidthparece funcionar solo para el ancho (que no puedo usar porque el texto de las etiquetas es dinámico).
ernesto
Tengo el mismo problema con el ajuste para ajustar el ancho
Julio
1
Terminé estableciendo la restricción de altura de la etiqueta en proporción a la altura de la supervista. Eso funciona bien siempre que establezca el parámetro "Líneas" de la etiqueta en IB en 0.
Dorian Roy
8

Este (y otros errores relacionados con las clases de tamaño de Xcode) me causaron un gran dolor recientemente, ya que tuve que pasar por un enorme archivo de guión gráfico pirateando cosas.

Para cualquier otra persona en esta posición, me gustaría agregar algo además de la respuesta de @ razor28 para aliviar el dolor.

En el archivo de encabezado de su subclase personalizada, utilice IBInspectablepara sus atributos de tiempo de ejecución. Esto hará que estos atributos sean accesibles desde el "Inspector de atributos", visualmente justo encima de la posición predeterminada para la configuración de fuentes.

Ejemplo de uso:

@interface MyCustomLabel : UILabel

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

@end

Esto producirá muy útilmente este resultado:

ingrese la descripción de la imagen aquí

Un beneficio adicional es que ahora no tenemos que agregar los atributos de tiempo de ejecución manualmente para cada etiqueta. Esto es lo más cerca que pude llegar al comportamiento previsto de XCode. Con suerte, una solución adecuada está en camino con iOS 9 este verano.

joakim
fuente
Agregué según sus instrucciones pero no puedo encontrar el acceso "Inspector de atributos" visualmente en el xib, así que, ¿cómo lograrlo?
Darshan Kunjadiya
3

Solución similar a la de @ razor28, pero creo que un poco más universal. Además, funciona bien en versiones anteriores de iOS.

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

Vitaly
fuente
Y claro, puede establecer la propiedad overrideFontName en Interface Builder que permite evitar escribir código innecesario
Vitaly
Esto no funcionó para mí, enumeré todas las fuentes instaladas dentro del método anterior y mi fuente se enumeró y asignó en la variable, pero no alteré la fuente que se muestra en la etiqueta del simulador y supongo que el dispositivo
Jules
3

El error sigue siendo válido en XCode 7.0 GM.

La solución de Razor28 provoca bucles infinitos en algunos casos. Mi experiencia ha sido su uso junto con SwipeView .

En cambio, te sugiero que:

1) Subclase UILabel y override setFont:

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

2) Configure la clase personalizada de sus UILabels y luego configure las clases de tamaño de fuente usando la fuente del sistema

ingrese la descripción de la imagen aquí

Foti Dim
fuente
@ Maarten1909 ¿Se refiere a la solución alternativa o al método sencillo de XCode? Si es el primero, ¿puede especificar la versión para que intente reproducirlo?
Foti Dim
Parece que he estado usando el nombre de familia de fuentes incorrecto todo el tiempo. Resulta que, en tal caso, las fuentes se restablecen a una fuente del sistema con un tamaño de pinta 14.0. Culpa mía. ¡Pero podría ser útil para otros!
Berendschot
0

El problema persiste: no puede usar la función para configurar fuentes para diferentes clases de tamaño desde el generador de interfaces.

Simplemente configure la fuente según el dispositivo que desee, como se muestra a continuación:

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

Esto funciona perfectamente en todos los dispositivos.

Nirmit Dagly
fuente
0

Ninguno de estos funcionó para mí, pero esto sí. También debe utilizar la fuente del sistema en 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
fuente
0

Todavía no hay una respuesta correcta firmada. Este código funciona bien para mí. Primero debe deshabilitar el tamaño de fuente para las clases de tamaño en el generador de interfaces. En IB puede utilizar fuentes personalizadas.

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

Una combinación de algunas de las respuestas anteriores anteriores fue útil. Así es como resolví el error de IB a través de una extensión 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
fuente
-1

Estoy usando Swift, XCode 6.4. Entonces esto es lo que hice

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. Ir al diseñador -> Inspector de identidad -> Establecer la clase en ExUILabel

  2. Luego vaya al inspector de atributos en el diseñador y establezca el nombre de la fuente.

kakopappa
fuente
-1

Se me ocurrió una solución aún más rápida (supongo que siempre usas tu única fuente personalizada).

Cree una categoría para UILabel e inclúyala en archivos utilizando un guión gráfico con errores con clases de tamaño y configuración de fuente dedicada para varias clases:

@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

Simplemente use sus fuentes personalizadas en el guión gráfico. Cuando el intérprete con errores utilice la fuente del sistema en lugar de la suya, esta categoría la cambiará a su fuente personalizada.

Heps
fuente
-1: No es una buena idea anular métodos en una categoría como esta. Razón: stackoverflow.com/questions/5272451/… En su lugar, debería swizzle (aunque hacky) o subclase.
JRG-Developer
-6

Tuve el mismo problema y encontré uno no muy claro, ¡pero una buena solución!

  1. Primero, establezca el tamaño de fuente requerido en el guión gráfico con el nombre de fuente del sistema.
  2. Luego, a esta etiqueta le asigna una etiqueta de 100 a 110 (o más, pero 10 siempre fue suficiente para mí en un controlador de vista).
  3. Luego coloque este código en el archivo fuente de su VC y no olvide cambiar el nombre de la fuente. Codifique en rápido.
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
fuente
1
No uses etiquetas. Utilice IBOutlets. Las etiquetas son malas. Consulte la sesión 231 de la WWDC 2015: “Si está usando Ver con etiqueta o Establecer etiqueta UIView API y código de envío, lo alentaré a que se aleje de eso. Como reemplazo de esto, declare propiedades en sus clases, y luego tendrá conexiones reales a esas vistas que necesite más adelante ".
KPM
Lamento que esta solución no sea perfecta, ¡pero funciona!
serg_ov
-8
  • agregue las fuentes proporcionadas por la aplicación en su aplicación .plist
  • agregue su archivo .ttf al elemento 0
  • utilícelo [UIFont fontWithName: "ejemplo.ttf" tamaño: 10]
usuario2612184
fuente
Ésta no es una respuesta a este problema. Obviamente, puede usar fuentes personalizadas mediante programación, pero mi pregunta se refiere a un error en iOS 8 que evita que las fuentes personalizadas cambien automáticamente según la clase de tamaño.
mnemia