Настраиваемый размер шрифта в классах размера Xcode 6 неправильно работает с настраиваемыми шрифтами


104

Xcode 6 имеет новую функцию, в которой шрифты и размеры шрифтов в UILabel, UITextFieldи UIButtonмогут быть установлены автоматически на основе класса размера текущей конфигурации устройства, прямо в раскадровке. Например, вы можете установить UILabelразмер шрифта 12 для конфигураций «любая ширина, компактная высота» (например, на iPhone в альбомной ориентации) и размер 18 для конфигураций «обычная ширина, обычная высота» (например, на iPad ). Более подробная информация доступна здесь:

developer.apple.com/size_class

Теоретически это отличная функция, потому что она может сделать ненужным программную установку различных шрифтов для функций пользовательского интерфейса в зависимости от конфигурации устройства. Прямо сейчас у меня есть условный код, который устанавливает шрифты в зависимости от типа устройства, но, очевидно, это означает, что я должен программно устанавливать шрифты во всем приложении. Так что я изначально был очень взволнован этой функцией, но я обнаружил, что у меня есть серьезная проблема фактического использования (возможно, ошибка). Обратите внимание, что я использую SDK 8 и устанавливаю минимальную цель развертывания iOS 8 , поэтому это не имеет ничего общего с совместимостью со старыми версиями iOS.

Проблема в следующем: если я устанавливаю разные размеры шрифта для разных классов размеров и использую шрифт «Системный», предоставляемый iOS , все работает, как ожидалось, и размеры шрифта меняются в зависимости от класса размера. Если я использую настраиваемый шрифт, предоставляемый моим приложением (да, он правильно настроен в моем пакете приложений, поскольку он работает программно), и устанавливаю настраиваемый шрифт для метки в раскадровке XCode 6 , это также работает, как ожидалось. Но когда я пытаюсь использовать разные размеры пользовательского шрифта для разных классов размеров в раскадровке, это внезапно не работает. Единственная разница в конфигурации - это шрифт, который я выбрал (пользовательский шрифт по сравнению с системным шрифтом). Вместо этого все шрифты отображаются наdevice и simulator в качестве системного шрифта по умолчанию с размером по умолчанию , независимо от класса размера (и я проверил через отладчик, что он заменяет системный шрифт на фактический, указанный в раскадровке). Таким образом, в основном, функция класса размера не работает для пользовательских шрифтов. Кроме того, что интересно, пользовательские шрифты на самом деле правильно отображают и регулируют размер в панели XCode 6 «Предварительный просмотр» для контроллера представления: он перестает работать только при запуске в реальной системе iOS (что заставляет меня думать, что я настраиваю его правильно) .

Я пробовал несколько разных пользовательских шрифтов, и, похоже, ни один из них не работает, но всегда работает, если я использую вместо этого «Системный».

В любом случае, кто-нибудь еще видел эту проблему в Xcode 6 ?

Любые идеи о том, является ли это ошибкой в ​​iOS 8, Xcode или что-то в этом роде

Я делаю не так?

Как я уже сказал, единственный обходной путь, который я нашел, - это продолжить программную установку шрифтов, как я делал примерно для трех версий iOS, потому что это действительно работает.

Но я бы хотел иметь возможность использовать эту функцию, если бы я мог заставить ее работать с пользовательскими шрифтами. Использование системного шрифта неприемлемо для нашего дизайна.


ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ: Начиная с Xcode 8.0, ошибка исправлена.


3
Я могу подтвердить, что это по-прежнему относится к выпуску магазина приложений xCode 6.1 (6A1052d).
jodm

3
Не решение, но мне удалось обойти проблему с помощью категории. Моя категория UILabel + Font.h (у меня тоже есть для кнопок) содержит следующий код. - (void) awakeFromNib {размер поплавка = [размер точки self.font]; self.font = [UIFont fontWithName: @ Размер "YourCustomFont": размер]; } Таким образом, это позволяет нам использовать классы размера с системным шрифтом для установки значений точки в разных классах размера, а категория гарантирует, что установлен правильный шрифт. Может быть, вы могли бы попробовать, пока Apple не выпустит обновление?
Daniel Retief Fourie

2
Все еще не решено в Xcode 6.3.1. Я зол на это.
Fury

7
По-прежнему не работает с Xcode 7 final. Не понимаю, почему Apple не исправляет это.
ernesto

5
все еще не работает над Xcode 7.2 iOS 9.2
Mojtaba

Ответы:


41

Быстрое исправление:

1) Установите шрифты как систему для классов размеров

Инспектор атрибутов меток

2) Подкласс UILabel и переопределите метод layoutSubviews, например:

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

}

Кстати, это очень удобный прием для иконических шрифтов.


Хотя это хороший обходной путь, если шрифт, используемый во всем приложении, один и тот же, это немного менее удобно, если вам нужно использовать разные шрифты в разных местах, потому что тогда у вас должны быть разные подклассы и т. Д. В моем случае я уже были настроены программные шрифты, и есть много разных способов сделать это. Но суть моего вопроса не в том, чтобы спросить, как это сделать программно: я хочу спросить, почему это не работает должным образом с использованием раскадровок (это ошибка Apple).
mnemia

@mnemia Согласен. Моя цель - показать один способ справиться с этой ошибкой.
razor28

1
Прекрасно работает. Однако вам не нужно создавать подкласс UILabel, вы можете просто переопределить этот метод в представлении, содержащем метку, и сделать это. self.label.font = ...Таким образом, вы можете использовать разные шрифты в разных местах.
KPM

Или вы можете настроить @IBInspectable для хранения имени шрифта, повторного использования одного и того же настраиваемого класса меток и запроса разных шрифтов непосредственно в раскадровке.
Крейг Груммитт

Есть ли подобное решение для UITextField?
Крис Байетт

17

Попробовав все, я в конце концов остановился на комбинации вышеперечисленных решений. Использование 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 позволяет установить размер шрифта в раскадровке
  • Он использует didSetнаблюдателя, чтобы избежать ошибок layoutSubviews()(бесконечный цикл для высоты строк динамического представления таблицы) и awakeFromNib()(см. Комментарий @ cocoaNoob)
  • Он использует классы размеров, а не идиому устройства, в надежде в конечном итоге использовать это с @IBDesignable
  • К сожалению, @IBDesignableне работает в traitCollectionсоответствии с этой другой статьей о стеке
  • Оператор переключения сбора признаков выполняется UIScreen.mainScreen()не в selfсоответствии с этой статьей стека

Лучший ответ, но, к сожалению, под голосованием .. Спасибо
Рохит Прадхан

9

Обходной путь для UILabel: сохранить тот же размер шрифта на всех размерных классов, но вместо того, чтобы изменить высоту этикетки соответственно в каждом Размер класса. На вашей этикетке должна быть включена автоматическая усадка. В моем случае это отлично сработало.


1
Я смог сделать то же самое, изменив ширину метки в каждом классе размера. Хороший совет!
dmzza

1
Я не заставил шрифт уменьшиться, установив меньшую фиксированную высоту для моего UILabel, как именно вы заставили его работать? adjustsFontSizeToFitWidthпохоже, работает только для ширины (которую я не могу использовать, потому что текст меток динамический).
Эрнесто

У меня такая же проблема с подогнать ширину
Жюль

1
Я закончил тем, что установил ограничение высоты метки пропорционально высоте супервизора. Это прекрасно работает, пока вы устанавливаете параметр «Lines» метки в IB равным 0.
Дориан Рой

8

Эта (и другая ошибка, связанная с Xcode - Size Classes) недавно вызвала у меня серьезное горе, так как мне пришлось пройти через огромный файл раскадровки, взламывая вещи.

Для всех, кто находится в этой должности, я хотел бы добавить что-то поверх ответа @razor28, чтобы облегчить боль.

В файле заголовка вашего настраиваемого подкласса используйте IBInspectableдля своих атрибутов времени выполнения. Это сделает эти атрибуты доступными из «Инспектора атрибутов», визуально прямо над положением по умолчанию для настроек шрифта.

Пример использования:

@interface MyCustomLabel : UILabel

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

@end

Это очень полезно для получения такого вывода:

введите описание изображения здесь

Дополнительным преимуществом является то, что теперь нам не нужно вручную добавлять атрибуты времени выполнения для каждой метки. Это самое близкое к предполагаемому поведению XCode. Надеюсь, этим летом в iOS 9 появится соответствующее исправление.


Я добавил в соответствии с вашей инструкцией, но я не могу визуально найти доступ к «Инспектору атрибутов» в xib, как этого добиться?
Даршан Кунджадия 02

3

Решение похоже на решение @razor28, но, на мой взгляд, более универсальное. Также отлично работает на старых версиях iOS.

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


И, конечно же, вы можете установить свойство overrideFontName в Interface Builder, что позволяет избежать написания ненужного кода
Виталий

Это не сработало для меня, я перечислил все установленные шрифты в указанном выше методе, и мой шрифт был указан и назначен в переменной, но не изменил отображение шрифта на этикетке на симуляторе, и я предполагаю, что устройство
Жюль

3

Ошибка все еще актуальна в XCode 7.0 GM.

Решение Razor28 в некоторых случаях вызывает бесконечные циклы. По моему опыту, он использовался вместе со SwipeView .

Вместо этого я предлагаю вам:

1) Подкласс UILabel и переопределить setFont:

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

2) Установите собственный класс ваших UILabels, а затем установите классы размера шрифта с помощью системного шрифта

введите описание изображения здесь


@ Maarten1909 Вы имеете в виду обходной путь или простой способ XCode? Если первый, не могли бы вы указать версию, чтобы я попытался ее воспроизвести?
Фоти Дим

Кажется, я все время использовал неправильное название семейства шрифтов. Оказывается, в таком случае шрифты сбрасываются на системный шрифт размером 14,0 пинты. Виноват. Но может быть полезно другим!
Berendschot

0

Проблема по-прежнему заключается в том, что вы не можете использовать эту функцию для установки шрифтов для разных классов размера из конструктора интерфейса.

Просто установите шрифт в соответствии с нужным устройством, как показано ниже:

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

Это отлично работает на всех устройствах.


0

У меня ничего из этого не сработало, но это сработало. Также необходимо использовать системный шрифт в 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

Все еще нет подписанного правильного ответа. Этот код мне подходит. Вы должны сначала отключить размер шрифта для классов размеров в построителе интерфейса. В IB можно использовать собственный шрифт.

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

Сочетание некоторых из приведенных выше ответов было полезно. Вот как я решил ошибку IB с помощью расширения 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)")
            }
        }
    }
}

-1

Я использую Swift, XCode 6.4. Так вот что я сделал

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. Перейти к конструктору -> Инспектор идентичности -> Установите для класса ExUILabel

  2. Затем перейдите в инспектор атрибутов в дизайнере и установите имя шрифта.


-1

Я придумал еще более быстрое исправление (я предполагаю, что вы всегда используете свой собственный шрифт).

Создайте категорию для UILabel и включите в файлы с помощью раскадровки с ошибками с классами размеров и настройками выделенного шрифта для различных классов:

@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

Просто используйте свои собственные шрифты в раскадровке. Когда ошибочный интерпретатор будет использовать системный шрифт вместо вашего, эта категория переключит его на ваш собственный шрифт.


-1: Не рекомендуется переопределять методы в подобной категории. Причина: stackoverflow.com/questions/5272451/… Вместо этого следует swizzle (хоть и хакерский) или подкласс.
JRG-Developer

-6

У меня была такая же проблема, и я нашел одно не совсем ясное, но прекрасное решение!

  1. Сначала вы устанавливаете требуемый размер шрифта в раскадровке с именем системного шрифта.
  2. Затем этой метке вы назначаете тег от 100 до 110 (или больше, но мне всегда хватало 10 в одном контроллере представления).
  3. Затем поместите этот код в исходный файл вашего ВК и не забудьте изменить имя шрифта. Кодируйте быстро.
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
Не используйте теги. Используйте IBOutlets. Теги плохие. См. Сессию 231 WWDC 2015: «Если вы используете View With Tag или Set Tag UIView API и код доставки, я призываю вас отказаться от этого. Вместо этого объявите свойства в своих классах, и тогда у вас будут реальные связи с теми представлениями, которые вам понадобятся позже ».
KPM

Мне жаль, что это решение не идеально, но оно работает!
serg_ov

-8
  • добавить шрифты, предоставленные приложением, в ваш app .plist
  • добавьте ваш .ttf файл в элемент 0
  • используйте его [UIFont fontWithName: "example.ttf" size: 10]

Это не ответ на эту проблему. Очевидно, вы можете использовать пользовательские шрифты программно, но мой вопрос касается ошибки в iOS 8, которая не позволяет пользовательским шрифтам автоматически изменяться в зависимости от класса размера.
mnemia
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.