【问题标题】:iOS 13: How can I tweak the leading / descend / line height of a custom font in UIKit / SwiftUIiOS 13:如何在 UIKit/SwiftUI 中调整自定义字体的前导/下降/行高
【发布时间】:2020-12-16 17:46:40
【问题描述】:

我正在使用自定义字体,并且不知何故渲染搞砸了行高,可能是因为配置错误 descentleading (?),所以 g 和 j 在渲染的最后一行被截断文本。我认为这可能是这种特定字体的问题,因为 Sketch 也暴露了相关字体的类似问题,但我觉得我对印刷测量或字体的了解不够。我发现Typographic Concepts 上的这个 Apple 文档页面非常有见地。

我使用 FontLab 的测试版本查看了字体本身,顺便说一句,我第一次使用它 - 所以我几乎不知道我在看什么。似乎 g 低于配置的descent,这似乎是最后一行。 (?)(参见:Character view in FontLab, showing the descend of the g

通过lineSpacing,我可以仅调整行本身之间的距离,以在前几行中解决此问题。我知道 iOS 14 将带来一种在 SwiftUI 中修改 Textleading 的方法。但我需要以 iOS 13 为目标,所以这无济于事。

我也尝试过 SwiftUI 的 Text、普通的 UILabel.textUILabel.attributedText 自定义段落样式,但我在那里调整的任何内容似乎都没有缓解这个问题。 视图甚至没有剪裁。只是在框架中添加填充根本没有帮助。它增加了距离,但 g 和 j 仍然被切断。

我能做什么?子类 UILabel 并覆盖 intrinsicContentSize 以添加一些额外的空间,当最后一行有 g 和 j 时?感觉 a) 脏 b) 考虑到填充没有帮助,它可能无法解决问题?

字体本身是这里的问题吗?我可以以某种方式修补字体而不使其变得更糟吗?

当我使用较低级别的 API 时,有什么方法可以修改字体的前导或下降高度?似乎我可以去CoreText,因为CTFontCreateCopyWithAttributes(_:_:_:_:)是一个候选人,如果我可以通过属性修改前导、行距或下降?可以或猴子修补/调酒而不用膝盖射击自己吗?我应该只提交雷达反馈吗?

【问题讨论】:

  • 任何指向可复制字体的链接?
  • @agibson007:这里有问题的字体是Edmondsans,这是一种专有/商业字体。
  • 刚刚更新了我的答案,因为我有更多时间来测试系统中包含的大多数字体。我知道这很严重,但我真的认为这是字体文件问题,而不是 Apple 方面的错误。也许这是一个错误。谁知道。 UILabel 不使用核心文本我不认为所以这些功能可能只会被证明很有帮助。希望这两种解决方案之一有所帮助。
  • 在这里报告我自己的问题:最后我联系了设计师,他可以为我提供不同版本的字体文件,这不再暴露问题。跨度>

标签: ios swift fonts uikit uifont


【解决方案1】:

我知道您在问什么,因为我在使用自定义字体时遇到了同样的问题。我将提供两种解决方案。在我自己的项目中,我按照您的建议覆盖了 intrinsicContentSize 并为高度和宽度添加了一个填充乘数。在我的例子中,字体是面向用户的,所以我有一个包含所有相关信息的结构。仅供参考 Chalkduster 在系统和剪辑中。我也相信这都是字体文件本身造成的。

解决方案 1: 示例:

struct UserFont{
    var name : String
    var displayName : String
    var widthMultiplier : CGFloat
    var heightMultiplier : CGFloat
}   

然后在我的 UILabel 中,我将其子类化以使用这两个指标

@IBDesignable
class MultiplierUILabel: UILabel {
    @IBInspectable var widthPaddingMultiplier : CGFloat = 1
    @IBInspectable var heightPaddingMultiplier : CGFloat = 1
    override var intrinsicContentSize: CGSize{
       return CGSize(width: super.intrinsicContentSize.width * widthPaddingMultiplier, height: super.intrinsicContentSize.height * heightPaddingMultiplier)
    }
}

这对我来说是最简单的实现,因为我找到了相应的字体和乘数比例。

解决方案 2:
您可以通过测量字形边界和调整原点 y 来使绘制稍微高一些。例如,这修复了系统中包含的 Chalkduster 字体的剪辑。

@IBDesignable
class PaddingUILabel: UILabel {
    override func drawText(in rect:CGRect) {
        //hello
        guard let labelText = text else {  return super.drawText(in: rect) }
        //just some breathing room
        let info = boundsForAttrString(str: labelText, font: self.font!, kern: .leastNormalMagnitude)
        let glyph = info.glyph
        var newRect = rect
        let glyphPadding = -(glyph.origin.y)
        if glyphPadding - info.descent > 1 && info.descent != 0{
            newRect.origin.y -= glyphPadding/2
        }else{
            if info.descent != 0{
                newRect.origin.y += (info.descent - glyphPadding)/2
            }
        }
        super.drawText(in: newRect)
    }
    
    func boundsForAttrString(str:String,font:UIFont,kern:CGFloat)->(glyph:CGRect,descent:CGFloat){
        let attr = NSAttributedString(string: str, attributes: [.font:font,.kern:kern])
        let line = CTLineCreateWithAttributedString(attr)
        var ascent : CGFloat = 0
        var descent : CGFloat = 0
        var leading : CGFloat = 0
        
        CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
        let glyph = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds).integral
        return (glyph,leading != 0 ? descent : 0)
    }
}

解决方案 2 的结果: 系统

PaddingUILabel 使用字形边界

【讨论】:

  • 哇哦,谢谢你的详细解答!使用@IBDesignable 来检查结果/呈现预览也是一个聪明的主意。
  • 它如何与您的自定义字体一起使用?只是好奇,因为我无法测试它。
  • 在我使用带有属性字符串的UILabelUITextView 通过UIViewRepresentable 的所有地方,我得到了它与解决方案1 ​​非常相似的解决方法。在某些地方我已经开始下到CoreText进行测量。但我只是希望有解决方案让它与Text 一起使用,因为它更容易与动态文本和本地化一起使用。 iOS 13 和 iOS 14 之间也有一些不同之处。
【解决方案2】:

你需要使用NSAttributedString而不是String来控制UILabel的行距。这是示例代码

let style = NSMutableParagraphStyle()
style.lineSpacing = 20
let string  = NSMutableAttributedString(string: "The quick brown fox jumps over the lazy dog, The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog")
string.addAttribute(.paragraphStyle, value: style, range: NSMakeRange(0, string.length))
let label = UILabel(frame: CGRect(x: 20, y: 100, width: 300, height: 500))
label.attributedText = string
label.numberOfLines = 0
self.view.addSubview(label)

输出

【讨论】:

  • 感谢您的回答,不胜感激。不幸的是,正如我在我的问题中所讨论的,它在这里没有帮助解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-14
  • 2021-07-17
  • 2021-01-05
  • 1970-01-01
  • 1970-01-01
  • 2019-11-14
  • 1970-01-01
相关资源
最近更新 更多