Xcode 6大小类中的自定义字体大小与自定义字体无法正常工作

Xcode 6 有一个新特性,UILabelUITextFieldUIButton中的 字体字体大小可以根据当前设备配置的大小类自动设置,就在故事板中。例如,你可以在“任意宽度,紧凑高度”配置上设置一个 UILabel使用字体大小12,在“常规宽度,常规高度”配置上设置字体大小18(例如在 IPad上)。你可在此浏览更多资料:

Developer.apple.com/size_class

这在理论上是一个很棒的特性,因为它可能使得不必根据设备配置以编程方式在 UI 特性上设置不同的字体。现在,我有一些基于设备类型设置字体的条件代码,但很明显,这意味着我必须在整个应用程序的每个地方以编程方式设置字体。因此,最初我对这个特性非常兴奋,但是我发现它在实际使用中有一个严重的问题(可能是一个 bug)。请注意,我是根据 SDK 8构建的,并设置了 IOS8的最小部署目标,所以这与与旧版本 iOS 的兼容性无关。

问题是: 如果我为不同的大小类设置不同的字体大小,并使用 IOS提供的 “系统”字体,一切都按预期工作,并且字体大小根据大小类变化。如果我使用我的应用程序提供的自定义字体(是的,我已经在我的捆绑中正确地设置了它,因为它是以编程方式工作的) ,并将自定义字体设置为 XCode 6故事板中的 标签,那么它也会按预期工作。但是当我尝试为不同大小的类使用不同大小的自定义字体时,在故事板中,它突然不起作用了。配置的唯一区别是我选择的字体(自定义字体与系统字体)。相反,所有的字体显示在 装置和模拟器作为 默认系统字体默认大小,不管大小类(我通过调试器验证,它是替换系统字体的实际指定的故事板)。所以基本上,自定义字体的大小类特性似乎被破坏了。另外,有趣的是,自定义字体实际上在视图控制器的 XCode 6“ Preview”面板中正确地显示和调整大小: 它只有在实际的 iOS 系统上运行时才会停止工作(这让我觉得我正在正确地配置它)。

我尝试了多种不同的自定义字体,它似乎不适合其中任何一种,但它总是工作,如果我使用“系统”而不是。

不管怎样,还有人在 Xcode 6中看到过这个问题吗?

关于这是不是 iOS8,Xcode 或者其他什么系统的错误,你有什么想法吗

我做错了吗?

正如我所说,我找到的唯一解决办法是继续像我在大约三个版本的 iOS 中那样通过编程设置字体,因为这确实有效。

但我希望能够使用这一功能,如果我可以让它与自定义字体的工作。使用系统字体对于我们的设计是不可接受的。


附加信息: 从 Xcode 8.0开始,bug 已经修复了。

16523 次浏览
  • add Fonts provided by application into your app .plist
  • add your .ttf file to item 0
  • use it [UIFont fontWithName:"example.ttf" size:10]

Fast fix:

1) Set fonts as System for size classes

Label attributes inspector

2) Subclass UILabel and override "layoutSubviews" method like:

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


}

By the way, it is a very convenient technique for iconic fonts

Workaround for UILabel: keep the same font size on all Size Classes, but instead change your label height accordingly in each Size Class. Your label must have autoshrink enabled. It worked nicely in my case.

Similar solution to @razor28's one but I think a little more universal. Also, works fine on older iOS versions

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

This (and other Xcode - Size Classes related) bug caused me some serious grief recently as I had to go through a huge storyboard file hacking things away.

For anyone else in this position, I'd like to add something on top of @razor28's answer to ease the pain.

In the header file of your custom subclass, use IBInspectable for your runtime attributes. This will make these attributes accessible from the "Attributes Inspector", visually right above the default position for font settings.

Example use:

@interface MyCustomLabel : UILabel


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


@end

This will very helpfully produce this output:

enter image description here

An added benefit is that now we don't have to add the runtime attributes manually for each label. This is the closest I could get to XCode's intended behaviour. Hopefully a proper fix is on its way with iOS 9 this summer.

I had the same problem and found one not really clear, but fine solution!

  1. First you set the required font size in storyboard with system font name.
  2. Then to this label you assign a tag from 100 to 110 (or more, but 10 was always enough for me in one view controller).
  3. Then put this code to your VC's source file and don't forget to change font name. Code in swift.
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)
}
}
}

The bug is still valid in XCode 7.0 GM.

Razor28's solution causes infinite loops in some cases. My experience has been with using it in conjunction with SwipeView.

Instead, I suggest that you:

1) Subclass UILabel and override setFont:

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

2) Set the custom class of your UILabels and then set the font size classes by using System font

enter image description here

I am using Swift, XCode 6.4. So this is what I did

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 -> Set the class to ExUILabel

  2. Then go to Attribute inspector in designer and set the font name.

The problem is still there that you cannot use the feature to set Fonts for different size classes from interface builder.

Just set font based on the device you want just like below:

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

This works perfectly on all devices.

None of these worked for me, but this did. You also need to use the system font 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

After trying everything, I eventually settled on a combination of the above solutions. Using 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 lets you set the font size in the Storyboard
  • It uses a didSet observer, to avoid the pitfalls from layoutSubviews() (infinite loop for dynamic table view row heights) and awakeFromNib() (see @cocoaNoob's comment)
  • It uses size classes rather than the device idiom, in hopes of eventually using this with @IBDesignable
  • Sadly, @IBDesignable doesn't work with traitCollection according to this other stack article
  • The trait collection switch statement is performed on UIScreen.mainScreen() rather than self per this stack article

I've come up with even faster fix (I assume you always use your one custom font).

Create a category for UILabel and include in files using buggy storyboard with size classes and dedicated font setting for various 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

Just use your custom fonts in storyboard. When buggy interpreter will use system font instead your own this category will switch it to your custom font.

Still no signed right answer. This code works fine for me. You must disable font size for size classes in interface builder first. In IB you can use custom font.

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


}
}

A combination of some of the later answers above were helpful. Here's how I solved the IB bug via a Swift UILabel extension:

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