Custom Font Sizing in Xcode 6 Size Classes not working properly with Custom Fonts

爷,独闯天下 提交于 2019-11-27 02:46:52
razor28

Fast fix:

1) Set fonts as System for size classes

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

Robert Chen

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

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.

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:

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.

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

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

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

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

    }
}
benjam

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

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.

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.

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)
        }
    }
}
user2612184
  • add Fonts provided by application into your app .plist
  • add your .ttf file to item 0
  • use it [UIFont fontWithName:"example.ttf" size:10]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!