iOS 13:如何在UIKit/SwiftUI中调整自定义字体的前导/下移/行高

iOS 13:如何在UIKit/SwiftUI中调整自定义字体的前导/下移/行高,ios,swift,fonts,uikit,uifont,Ios,Swift,Fonts,Uikit,Uifont,我使用的是自定义字体,但渲染会以某种方式扭曲行高,这可能是因为错误配置了下降或前导(?),因此在渲染文本的最后一行中,g和j被截断。我认为这可能是这个特殊字体的一个问题,因为Sketch也暴露了这个问题中字体的类似问题,但我觉得我对排版测量或字体理解不够。我发现这个苹果文档页面非常有洞察力 我用FontLab的测试版本查看了字体本身,顺便说一句,我第一次使用了FontLab,所以我对我正在查看的内容几乎一无所知。看起来g确实在下降到配置的下降以下,这似乎是最后一行。(?)(见:) 通过line

我使用的是自定义字体,但渲染会以某种方式扭曲行高,这可能是因为错误配置了
下降
前导
(?),因此在渲染文本的最后一行中,g和j被截断。我认为这可能是这个特殊字体的一个问题,因为Sketch也暴露了这个问题中字体的类似问题,但我觉得我对排版测量或字体理解不够。我发现这个苹果文档页面非常有洞察力

我用FontLab的测试版本查看了字体本身,顺便说一句,我第一次使用了FontLab,所以我对我正在查看的内容几乎一无所知。看起来g确实在下降到配置的
下降
以下,这似乎是最后一行。(?)(见:)

通过
linespace
我可以调整两行之间的距离,从而在前几行中解决这一问题。我知道iOS 14将带来一种在SwiftUI中修改
文本的方法。但我需要瞄准iOS 13,所以这没有帮助

我还尝试了SwiftUI的
文本
,一种普通的
UILabel.Text
UILabel.attributedText
,并使用了自定义的段落样式,但我在其中所做的任何调整似乎都不能缓解问题。 视图甚至没有被剪裁。仅仅在框架中添加填充根本没有帮助。它增加了距离,但g和j仍然被切割

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

字体本身就是问题所在吗?我能在不让字体变得更糟的情况下修补字体吗


当我使用较低级别的API时,有没有办法修改字体的前导高度或下降高度?似乎我可以转到CoreText,因为
CTFontCreateCopyWithAttributes(:::::)
是一个候选,如果我可以通过属性修改前导、行间距或下降?你或猴子能在不射中自己膝盖的情况下修补/刷东西吗?我是否应该提交一个雷达反馈?

您需要使用
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)
发出


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

解决方案1: 例如:

然后在我的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,您可能能够使绘制发生得稍微高一些。例如,这修复了系统中包含的粉笔喷粉字体的剪裁

@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的结果: 系统

使用glyph边界的PaddingUILabel

有可复制字体的链接吗?@agibson007:这里讨论的字体是,这是一种专有/商业字体。刚刚更新了我的答案,因为我有更多的时间对系统中包含的大多数字体进行测试。我知道这让人恼火,但我真的认为这是字体文件的问题,而不是苹果方面的错误。也许是个虫子。谁知道呢。UILabel没有在引擎盖下使用核心文本,我不这么认为,这些函数可能只会证明是非常有用的。希望这两个解决方案中的一个能有所帮助。回到这里,我回答了我自己的问题:最终我找到了设计师,他可以为我提供不同版本的字体文件,这不再暴露问题。谢谢你的回答,谢谢。不幸的是,正如我在问题中所讨论的,这对解决这个问题没有帮助。噢,哇,谢谢你的详细回答!使用
@IBDesignable
检查结果/呈现预览也是一个聪明的主意。它是如何与您的自定义字体一起工作的?只是好奇,因为我无法测试它。在我使用
UILabel
UITextView
通过
UIViewRepresentable
使用属性字符串的所有地方,我都使用了与解决方案1非常类似的解决方法。在一些地方,我开始到CoreText进行测量。但我只希望有一个解决方案,使它与
文本一起工作,因为它更容易与动态文本和本地化一起使用。另外,iOS 13和iOS 14之间的某些方面略有不同。
@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)
    }
}