swift - 无法子类化 UIFont

标签 swift uikit uifont

我在 iOS 应用程序中使用自定义字体,并设置了如下字体:

private enum MalloryProWeight: String {
 case book = "MalloryMPCompact-Book"
 case medium = "MalloryMPCompact-Medium"
 case bold = "MalloryMPCompact-Bold"}


extension UIFont {
enum Caption {
    private static var bookFont: UIFont {
        UIFont(name: MalloryProWeight.book.rawValue, size: 1)!
    }

    private static var mediumFont: UIFont {
        UIFont(name: MalloryProWeight.medium.rawValue, size: 1)!
    }

    private static var boldFont: UIFont {
        UIFont(name: MalloryProWeight.bold.rawValue, size: 1)!
    }

    static var book: UIFont {
        return bookFont.withSize(10)
    }

    static var medium: UIFont {
        mediumFont.withSize(10)
    }

    static var bold: UIFont {
        boldFont.withSize(10)
    }
}

这样在调用站点我可以执行以下操作:

UIFont.Caption.bold

这很有效;我有一个 NSAttributed 扩展,它接受 UIFont 和颜色并返回一个属性字符串 = 所以它非常适合。

但是,我现在需要在每种字体上设置 LetterSpacing 和 LineHeight。

我不想去更新 NSAttributed 扩展来获取这些值来设置它们 - 我理想地希望它们可以从 UIFont 访问

所以,我尝试子类化 UIFont 以向其中添加我自己的属性 - 就像这样:

class MrDMyCustomFontFont: UIFont {
    var letterSpacing: Double?
}

像这样使用它

private static var boldFont: UIFont {
    MrDMyCustomFontFont(name: MalloryProWeight.bold.rawValue, size: 1)!
}

但是编译器会提示,我不确定如何解决它:

Argument passed to call that takes no arguments

所以我的问题分为两部分:

  1. 如何在 UIFont 上添加我自己的自定义属性(并在每个实例的基础上设置它)
  2. 否则我如何正确子类化 UIFont 以便我可以在那里添加我自己的属性?

谢谢!

最佳答案

您无法子类化 UIFont,因为它通过 UICTFont 桥接到 CTFont。这就是为什么 init 方法在 header 中被标记为“未继承”。这不是一堂普通的类(class)。

您可以轻松地向 UIFont 添加新属性,但它不会按照您希望的方式工作。这正是您所要求的:每个实例。但它不会被复制,因此从 boldFont.withSize(10) 返回的实例不会具有与 boldFont 相同的值。如果您想要代码,请按以下步骤操作:

private var letterSpacingKey: String? = nil

extension UIFont {
    var letterSpacing: Double? {
        get {
            (objc_getAssociatedObject(self, &letterSpacingKey) as? NSNumber)?.doubleValue
        }
        set {
            objc_setAssociatedObject(self, &letterSpacingKey, newValue.map(NSNumber.init(value:)),
                                     .OBJC_ASSOCIATION_RETAIN)
        }
    }
}

然后你就可以设置它:

let font = UIFont.boldSystemFont(ofSize: 1)
font.letterSpacing = 1
print(font.letterSpacing) // Optional(1)

但是只要创建派生字体,您就会丢失它:

let newFont = font.withSize(10)
print(newFont.letterSpacing) // nil

所以我认为你不想要这样。

但其中大部分内容并没有真正意义。你会用这些属性做什么? “字母间距”不是字体特征;而是字体特征。这是布局/风格特征。谎报字体的高度也可能是错误的工具;配置通常也是段落特征。

您可能想要的是一个“样式”,它可以跟踪所有有问题的内容(字体、间距、段落样式等),并且可以应用于 AttributedString。幸运的是,iOS 15+ 中已经存在:AttributeContainer。在 iOS 15 之前,您只能使用 [NSAttributedString.Key: Any]

然后,您可以直接合并容器/字典(这正是它的设计工作方式),而不是使用 (NS)AttributedString 扩展来合并字体。

extension AttributeContainer {
    enum Caption {

        private static var boldAttributes: AttributeContainer {
            var container = AttributeContainer()
            container.font = UIFont(name: MalloryProWeight.bold.rawValue, size: 1)!
            container.expansion = 1
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineSpacing = 1.5
            container.paragraphStyle = paragraphStyle
            return container
        }

        static var bold: AttributeContainer {
            var attributes = boldAttributes
            attributes.font = boldAttributes.font.withsize(10)
            return attributes
        }
    }
}

关于swift - 无法子类化 UIFont,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72972953/

相关文章:

iOS 如何检测后台通话状态?

swift - 现在 Swift 4 中的 filePrivate 有什么意义?

iOS9 popover 总是指向 anchor 的左上角

ios - 是否可以获取与键盘关联的文本字段?

swift - 多个窗口 Controller - swift

ios - 将 slider 分配给水平分页(如 Instagram) - UISlider 到 UIScrollView

ios - subview 如何识别父 View (iPhone)的设置属性?

objective-c - 更改控件使用的默认 systemFont

objective-c - UIFont fontWithName 返回 nil

iphone - 为什么 Wingdings 在 iOS 5 上无法运行?为什么我无法在 iPhone 应用程序中使用 Wingdings?