Xcode 6 大小类中的自定义字体大小无法与自定义字体一起正常工作
Posted
技术标签:
【中文标题】Xcode 6 大小类中的自定义字体大小无法与自定义字体一起正常工作【英文标题】:Custom Font Sizing in Xcode 6 Size Classes not working properly with Custom Fonts 【发布时间】:2014-11-27 18:59:27 【问题描述】:Xcode 6 有一个新功能,其中UILabel
、UITextField
和UIButton
中的字体 和字体大小根据当前设备配置的大小等级自动设置,就在情节提要中。例如,您可以设置UILabel
以在“任意宽度、紧凑高度”(例如在横向 iPhone 上)配置上使用字体大小 12,在“常规宽度、常规高度”配置(例如在 iPad)。更多信息可在此处获得:
developer.apple.com/size_class
理论上这是一个很棒的功能,因为它可以使您无需根据设备配置以编程方式在 UI 功能上设置不同的字体。现在,我有一些条件代码可以根据设备类型设置字体,但显然,这意味着我必须在整个应用程序的任何地方以编程方式设置字体。所以一开始我对这个功能非常兴奋,但是我发现它对我来说在实际使用中存在严重的问题(可能是一个错误)。请注意,我是针对 SDK 8 构建的,并设置了 iOS 8 的最低部署目标,因此这与与旧版本 ios 的兼容性无关。
问题是这样的: 如果我为不同的size classes设置不同的字体大小,并使用iOS提供的"System" font,一切都会按预期工作,并且字体大小会根据size class而变化.如果我使用我的应用程序提供的自定义字体(是的,我在我的应用程序包中正确设置了它,因为它以编程方式工作)并将自定义字体设置为 XCode 中的 label 6 故事板,也可以按预期工作。但是当我尝试为不同大小的类使用不同大小的自定义字体时,在情节提要中,它突然不起作用。配置的唯一区别是我选择的字体(自定义字体与系统字体)。相反,所有字体在设备和模拟器上显示为默认系统字体在默认大小,无论大小类(和我通过调试器验证它正在用系统字体替换情节提要中指定的实际字体)。所以基本上,自定义字体的大小类功能似乎被破坏了。此外,有趣的是,自定义字体实际上在视图控制器的 XCode 6“预览”窗格中正确显示和调整大小:它仅在实际 iOS 系统上运行时才停止工作(这让我认为我正在正确配置它) .
我尝试了多种不同的自定义字体,但似乎对它们都不起作用,但如果我改用“系统”,它总是有效的。
还有其他人在 Xcode 6 中看到过这个问题吗?
关于这是否是 iOS 8、Xcode 或其他问题的任何想法
我做错了吗?
正如我所说,我发现的唯一解决方法是继续以编程方式设置字体,就像我在大约三个 iOS 版本中所做的那样,因为这确实有效。
但如果我可以让它与自定义字体一起使用,我希望能够使用此功能。我们的设计不接受使用 System 字体。
附加信息: 从 Xcode 8.0 开始,该错误已修复。
【问题讨论】:
我可以确认在 xCode 6.1 (6A1052d) 应用商店版本中仍然是这种情况。 不是解决方案,但我已经设法使用类别解决了这个问题。我的 UILabel+Font.h 类别(我也有一个用于按钮的)包含以下代码。 -(void)awakeFromNib float size = [self.font pointSize]; self.font = [UIFont fontWithName:@"YourCustomFont" size:size];因此,这允许我们使用带有系统字体的尺寸类来设置不同尺寸类中的点值,并且类别确保设置了正确的字体。也许您可以在 Apple 发布更新之前尝试一下? Xcode 6.3.1 中仍未解决。我对此很生气。 仍然无法使用 Xcode 7 final。不明白为什么 Apple 不修复它。 仍然无法在 Xcode 7.2 iOS 9.2 上运行 【参考方案1】: 将应用提供的字体添加到您的应用.plist中 将您的 .ttf 文件添加到第 0 项 使用它[UIFont fontWithName:"example.ttf" size:10]【讨论】:
这不是这个问题的答案。显然您可以通过编程方式使用自定义字体,但我的问题指的是 iOS 8 中的一个错误,它阻止自定义字体根据大小类自动更改。【参考方案2】:快速修复:
1) 将字体设置为 System for size classes
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];
顺便说一句,对于标志性字体来说,这是一种非常方便的技术
【讨论】:
虽然如果整个应用程序使用的字体相同,这是一个很好的解决方法,但如果您需要在不同的地方使用不同的字体,这会不太方便,因为您必须有不同的子类,等等。就我而言,我已经设置了程序字体,并且有很多不同的方法可以做到这一点。但我的问题不是要问如何以编程方式执行此操作:而是要问为什么使用 Storyboards 不能正常工作(这是 Apple 的错误)。 @mnemia 我同意。我的目的只是展示一种如何处理该错误的方法。 效果很好。不过,您不需要继承 UILabel,您可以在包含标签的视图中重写此方法并执行self.label.font = ...
这样您就可以在不同的地方使用不同的字体。
或者你可以设置一个@IBInspectable 来存储字体名称,重复使用相同的自定义标签类并直接在情节提要中请求不同的字体。
UITextField 有类似的解决方案吗?【参考方案3】:
解决方法 UILabel
:在所有尺寸类别上保持相同的字体大小,但在每个尺寸类别中相应地更改标签高度。您的标签必须启用自动收缩功能。在我的情况下效果很好。
【讨论】:
我可以通过更改每个尺寸类中的标签宽度来做同样的事情。好提示! 我没有通过为我的 UILabel 设置一个较小的固定高度来缩小字体,你是如何让它工作的?adjustsFontSizeToFitWidth
似乎只适用于宽度(我不能使用,因为标签的文本是动态的)。
我在调整以适应宽度时遇到同样的问题
我最终将 Label 的高度约束设置为与 superview 的高度成比例。只要将 IB 中 Label 的“Lines”参数设置为 0,就可以正常工作。【参考方案4】:
与@razor28 的解决方案类似,但我认为更通用一点。此外,在较旧的 iOS 版本上也能正常工作
https://gist.github.com/softmaxsg/8a3ce30331f9f07f023e
【讨论】:
当然,您可以在 Interface Builder 中设置属性 overrideFontName 以避免编写不必要的代码 这对我不起作用,我在上述方法中列出了所有已安装的字体,我的字体被列出并分配在变量中,但没有改变标签上显示的字体模拟器,我假设设备【参考方案5】:这个(以及其他与 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 中直观地找到访问“属性检查器”,那么如何实现呢?【参考方案6】:我遇到了同样的问题,发现一个不是很清楚,但很好的解决方案!
-
首先您在情节提要中使用系统字体名称设置所需的字体大小。
然后为这个标签分配一个从 100 到 110 的标签(或更多,但在一个视图控制器中,10 对我来说总是足够的)。
然后把这段代码放到你的VC的源文件中,别忘了改字体名。快速编写代码。
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)
【讨论】:
不要使用标签。使用 IBOutlets。标签不好。请参阅 WWDC 2015 会议 231:“如果您使用 View With Tag 或 Set Tag UIView API 和交付代码,我将鼓励您远离它。作为对此的替代,在您的类上声明属性,然后您将与以后需要的那些视图建立真正的联系。” 我很遗憾这个解决方案并不完美,但它确实有效!【参考方案7】:该错误在 XCode 7.0 GM 中仍然有效。
Razor28 的解决方案在某些情况下会导致无限循环。我的经验是将它与SwipeView 结合使用。
相反,我建议你:
1) 子类 UILabel 并覆盖setFont:
- (void)setFont:(UIFont *)font
font = [UIFont fontWithName:(@"Montserrat") size:font.pointSize];
[super setFont:font];
2) 设置您的 UILabel 的自定义类,然后使用系统字体设置字体大小类
【讨论】:
@Maarten1909 您指的是解决方法还是直接的 XCode 方式?如果第一个可以请您指定版本以便我尝试复制它吗? 看来我一直在使用错误的字体系列名称。事实证明,在这种情况下,字体被重置为 14.0 pint-size 的系统字体。我的错。但它可能对其他人有用!【参考方案8】:我正在使用 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)
Goto Designer -> Identity Inspector -> 将类设置为 ExUILabel
然后进入设计器中的属性检查器并设置字体名称。
【讨论】:
【参考方案9】:问题仍然存在,您无法使用该功能从界面生成器中为不同大小的类设置字体。
只需根据您想要的设备设置字体,如下所示:
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]];
这在所有设备上都能完美运行。
【讨论】:
【参考方案10】:这些都不适合我,但确实如此。 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
【讨论】:
【参考方案11】:在尝试了一切之后,我最终确定了上述解决方案的组合。使用 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
可让您在 Storyboard 中设置字体大小
它使用didSet
观察者,以避免来自layoutSubviews()
(动态表格视图行高的无限循环)和awakeFromNib()
(参见@cocoaNoob 的评论)的陷阱
它使用大小类而不是设备习语,希望最终将其与@IBDesignable
一起使用
可悲的是,@IBDesignable
不适用于 traitCollection
根据其他 stack article
trait 集合 switch 语句是在 UIScreen.mainScreen()
而不是 self
上执行的 stack article
【讨论】:
最佳答案,但遗憾的是在投票中......谢谢【参考方案12】:我想出了更快的修复方法(我假设您总是使用一种自定义字体)。
为 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://***.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:在这样的类别中覆盖方法不是一个好主意。原因:***.com/questions/5272451/… 相反,应该 swizzle(虽然 hacky)或子类。【参考方案13】:仍然没有签名的正确答案。这段代码对我来说很好。您必须首先在界面生成器中禁用大小类的字体大小。在 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];
【讨论】:
【参考方案14】:上述一些后来的答案的组合很有帮助。以下是我通过 Swift UILabel 扩展解决 IB 错误的方法:
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)")
【讨论】:
以上是关于Xcode 6 大小类中的自定义字体大小无法与自定义字体一起正常工作的主要内容,如果未能解决你的问题,请参考以下文章
无法更改 Storyboard 中的自定义 UICollectionViewCell
iOS:Xcode 字体大小与 Adobe XD 字体大小