Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Xcode 6 has a new feature where fonts and font sizes in UILabel, UITextField, and UIButton can be set automatically based on the size class of the current device configuration, right in the storyboard. For example, you can set a UILabel to use font size 12 on "any width, compact height" (such as on iPhones in landscape) configurations and size 18 on "regular width, regular height" configurations (such as on iPads). More information is available here:

developer.apple.com/size_class

This is a great feature in theory because it could make it unnecessary to programmatically set different fonts on UI features based on the device configuration. Right now, I have some conditional code that sets the fonts based on the device type, but obviously, that means I have to set the fonts programmatically everywhere throughout the app. So I was initially really excited about this feature, but I found that it has a severe problem of actual usage for me (perhaps a bug). Note that I am building against SDK 8 and setting a minimum deployment target of iOS 8, so this has nothing to do with compatibility with old versions of iOS.

The problem is this: If I set different font sizes for different size classes and use the "System" font provided by iOS, everything works as expected, and the font sizes change based on the size class. If I use a custom font supplied by my application (yes, I have it set up correctly in my application bundle, as it works programmatically) and set the custom font to a label in an XCode 6 storyboard, that also works as expected. But when I try to use different sizes of the custom font for different size classes, in the storyboard, it suddenly doesn't work. The only difference in configuration is the font I've chosen (a custom one vs. the System font). Instead, all of the fonts show up on the device and simulator as the default system font at the default size, regardless of size class (and I verified via the debugger that it is substituting the system font for the actual one specified in the storyboard). So basically, the size class feature appears to be broken for custom fonts. Also, interestingly, the custom fonts actually display and adjust size properly in the XCode 6 "Preview" pane for the view controller: it stops working only when running on the actual iOS system (which makes me think that I'm configuring it correctly).

I tried multiple different custom fonts, and it doesn't seem to work for any of them, but it always works if I use "System" instead.

Anyway, has anyone else seen this problem in Xcode 6?

Any ideas on whether this is a bug in iOS 8, Xcode, or something

Am I doing wrong?

The only workaround I've found, as I said, is to continue to programmatically set the fonts like I have been for about three versions of iOS because that does work.

But I'd love to be able to use this feature if I could get it to work with custom fonts. Using the System font is not acceptable for our design.


ADDITIONAL INFO: As of Xcode 8.0, the bug is fixed.

like image 494
mnemia Avatar asked Oct 02 '14 18:10

mnemia


People also ask

How do I add a TTF font to Xcode?

To add a font file to your Xcode project, select File > Add Files to “Your Project Name” from the menu bar, or drag the file from Finder and drop it into your Xcode project. You can add True Type Font (. ttf) and Open Type Font (. otf) files.

How do I use different fonts in SwiftUI?

Setting upChoose the font that you like and download. Usually the file type of the fonts are TTF or OTF format. Once you have your font ready, drag the files to your project, you can put them inside a folder if you create a New Group (. Make sure to check Copy items if needed and check Add to targets.


2 Answers

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

like image 168
razor28 Avatar answered Oct 07 '22 01:10

razor28


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
like image 38
Robert Chen Avatar answered Oct 07 '22 00:10

Robert Chen