简体   繁体   English

如何使用核心文本和属性字符串伪造上标和下标?

[英]How can I fake superscript and subscript with Core Text and an Attributed String?

I'm using an NSMutableAttribtuedString in order to build a string with formatting, which I then pass to Core Text to render into a frame.我正在使用NSMutableAttribtuedString来构建一个带有格式的字符串,然后我将其传递给 Core Text 以呈现到一个框架中。 The problem is, that I need to use superscript and subscript.问题是,我需要使用上标和下标。 Unless these characters are available in the font (most fonts don't support it), then setting the property kCTSuperscriptAttributeName does nothing at all.除非这些字符在字体中可用(大多数字体不支持它),否则设置属性kCTSuperscriptAttributeName根本不起作用。

So I guess I'm left with the only option, which is to fake it by changing the font size and moving the base line.所以我想我只有唯一的选择,那就是通过改变字体大小和移动基线来伪造它。 I can do the font size bit, but don't know the code for altering the base line.我可以做字体大小位,但不知道更改基线的代码。 Can anyone help please?有人可以帮忙吗?

Thanks!谢谢!

EDIT: I'm thinking, considering the amount of time I have available to sort this problem, of editing a font so that it's given a subscript "2"... Either that or finding a built-in iPad font which does.编辑:我在想,考虑到我有多少时间可以解决这个问题,编辑字体以便给它一个下标“2”......或者找到一个内置的 iPad 字体。 Does anyone know of any serif font with a subscript "2" I can use?有谁知道我可以使用任何带有下标“2”的衬线字体?

There is no baseline setting amongst the CTParagraphStyleSpecifiers or the defined string attribute name constants. CTParagraphStyleSpecifiers或定义的字符串属性名称常量中没有基线设置。 I think it's therefore safe to conclude that CoreText does not itself support a baseline adjust property on text. 因此,我认为结论CoreText本身不支持文本的基线调整属性是安全的。 There's a reference made to baseline placement in CTTypesetter, but I can't tie that to any ability to vary the baseline over the course of a line in the iPad's CoreText. 在CTTypesetter中有一个对基线位置的引用,但我无法将其与任何能够在iPad的CoreText中的一行中改变基线的能力联系起来。

Hence, you probably need to interfere in the rendering process yourself. 因此,您可能需要自己干预渲染过程。 For example: 例如:

  • create a CTFramesetter, eg via CTFramesetterCreateWithAttributedString 创建一个CTFramesetter,例如通过CTFramesetterCreateWithAttributedString
  • get a CTFrame from that via CTFramesetterCreateFrame 通过CTFramesetterCreateFrame获取CTFramesetterCreateFrame
  • use CTFrameGetLineOrigins and CTFrameGetLines to get an array of CTLines and where they should be drawn (ie, the text with suitable paragraph/line breaks and all your other kerning/leading/other positioning text attributes applied) 使用CTFrameGetLineOriginsCTFrameGetLines来获取CTFrameGetLines数组以及它们应该被绘制的位置(即,具有合适的段落/换行符的文本以及应用的所有其他字距调整/前导/其他定位文本属性)
  • from those, for lines with no superscript or subscript, just use CTLineDraw and forget about it 从那些,对于没有上标或下标的行,只需使用CTLineDraw并忘记它
  • for those with superscript or subscript, use CTLineGetGlyphRuns to get an array of CTRun objects describing the various glyphs on the line 对于那些带有上标或下标的人,使用CTLineGetGlyphRuns获取一系列CTRun对象来描述该行上的各种字形
  • on each run, use CTRunGetStringIndices to determine which source characters are in the run; 在每次运行中,使用CTRunGetStringIndices来确定运行中的源字符; if none that you want to superscript or subscript are included, just use CTRunDraw to draw the thing 如果没有你想要的上标或下标,只需使用CTRunDraw绘制东西
  • otherwise, use CTRunGetGlyphs to break the run into individual glyphs and CTRunGetPositions to figure out where they would be drawn in the normal run of things 否则,使用CTRunGetGlyphs打破单个字形和CTRunGetPositions的运行,找出它们在正常运行中的绘制位置
  • use CGContextShowGlyphsAtPoint as appropriate, having tweaked the text matrix for those you want in superscript or subscript 适当地使用CGContextShowGlyphsAtPoint为上标或下标中的那些调整文本矩阵

I haven't yet found a way to query whether a font has the relevant hints for automatic superscript/subscript generation, which makes things a bit tricky. 我还没有找到一种方法来查询字体是否具有自动上标/下标生成的相关提示,这使得事情有点棘手。 If you're desperate and don't have a solution to that, it's probably easier just not to use CoreText's stuff at all — in which case you should probably define your own attribute (that's why [NS/CF]AttributedString allow arbitrary attributes to be applied, identified by string name) and use the normal NSString searching methods to identify regions that need to be printed in superscript or subscript from blind. 如果你是绝望的并且没有解决方案,那么根本不使用CoreText的东西可能更容易 - 在这种情况下你应该定义你自己的属性(这就是为什么[NS / CF] AttributedString允许任意属性应用,由字符串名称标识)并使用常规的NSString搜索方法来识别需要在上标或下标中打印的区域。

For performance reasons, binary search is probably the way to go on searching all lines, the runs within a line and the glyphs within a run for those you're interested in. Assuming you have a custom UIView subclass to draw CoreText content, it's probably smarter to do it ahead of time rather than upon every drawRect: (or the equivalent methods, if eg you're using a CATiledLayer). 出于性能原因,二进制搜索可能是继续搜索所有行,一行中的运行以及运行中的字形以供您感兴趣的方式。假设您有一个自定义UIView子类来绘制CoreText内容,它可能是更聪明地提前做,而不是每个drawRect :(或等效的方法,如果你使用CATiledLayer)。

Also, the CTRun methods have variants that request a pointer to a C array containing the things you're asking for copies of, possibly saving you a copy operation but not necessarily succeeding. 此外,CTRun方法的变体请求指向C数组的指针,该数组包含您要求复制的内容,可能会保存复制操作但不一定成功。 Check the documentation. 查看文档。 I've just made sure that I'm sketching a workable solution rather than necessarily plotting the absolutely optimal route through the CoreText API. 我刚刚确定我正在草拟一个可行的解决方案,而不是通过CoreText API绘制绝对最佳的路线。

Here is some code based on Tommy's outline that does the job quite well (tested on only single lines though). 这里有一些基于Tommy大纲的代码可以很好地完成工作(尽管只在单行测试)。 Set the baseline on your attributed string with @"MDBaselineAdjust" , and this code draws the line to offset , a CGPoint . 使用@"MDBaselineAdjust"在属性字符串上设置基线,此代码将线条绘制为offset CGPoint To get superscript, also lower the font size a notch. 要获得上标,也要将字体大小降低一个档次。 Preview of what's possible: http://cloud.mochidev.com/IfPF (the line that reads "[Xe] 4f 14 ...") 预览可能的内容: http//cloud.mochidev.com/IfPF (读写“[Xe] 4f 14 ...”的行)

Hope this helps :) 希望这可以帮助 :)

NSAttributedString *string = ...;
CGPoint origin = ...;

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, string.length), NULL, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), NULL);
CGPathRef path = CGPathCreateWithRect(CGRectMake(origin.x, origin.y, suggestedSize.width, suggestedSize.height), NULL);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, string.length), path, NULL);
NSArray *lines = (NSArray *)CTFrameGetLines(frame);
if (lines.count) {
    CGPoint *lineOrigins = malloc(lines.count * sizeof(CGPoint));
    CTFrameGetLineOrigins(frame, CFRangeMake(0, lines.count), lineOrigins);

    int i = 0;
    for (id aLine in lines) {
        NSArray *glyphRuns = (NSArray *)CTLineGetGlyphRuns((CTLineRef)aLine);

        CGFloat width = origin.x+lineOrigins[i].x-lineOrigins[0].x;

        for (id run in glyphRuns) {
            CFRange range = CTRunGetStringRange((CTRunRef)run);
            NSDictionary *dict = [string attributesAtIndex:range.location effectiveRange:NULL];
            CGFloat baselineAdjust = [[dict objectForKey:@"MDBaselineAdjust"] doubleValue];

            CGContextSetTextPosition(context, width, origin.y+baselineAdjust);

            CTRunDraw((CTRunRef)run, context, CFRangeMake(0, 0));
        }

        i++;
    }

    free(lineOrigins);
}
CFRelease(frame);
CGPathRelease(path);
CFRelease(framesetter);

` `

You can mimic subscripts now using TextKit in iOS7. 您现在可以使用iOS7中的TextKit模仿下标。 Example: 例:

NSMutableAttributedString *carbonDioxide = [[NSMutableAttributedString alloc] initWithString:@"CO2"];
[carbonDioxide addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:8] range:NSMakeRange(2, 1)];
[carbonDioxide addAttribute:NSBaselineOffsetAttributeName value:@(-2) range:NSMakeRange(2, 1)];

属性字符串输出的图像

I've been having trouble with this myself. 我自己一直遇到麻烦。 Apple's Core Text documentation claims that there has been support in iOS since version 3.2, but for some reason it still just doesn't work. Apple的Core Text文档声称自3.2版以来iOS一直受到支持,但由于某种原因它仍然无效。 Even in iOS 5... how very frustrating >.< 即使在iOS 5中......也非常令人沮丧>

I managed to find a workaround if you only really care about superscript or subscript numbers. 如果你真的只关心上标或下标数字,我设法找到了一个解决方法。 Say you have a block of text can might contain a "sub2" tag where you want a subscript number 2. Use NSRegularExpression to find the tags, and then use replacementStringForResult method on your regex object to replace each tag with unicode characters: 假设您有一个文本块可能包含“sub2”标记,您需要下标编号2.使用NSRegularExpression查找标记,然后在regex对象上使用replacementStringForResult方法将每个标记替换为unicode字符:

if ([match isEqualToString:@"<sub2/>"])
{
   replacement = @"₂";
}

If you use the OSX character viewer, you can drop unicode characters right into your code. 如果使用OSX字符查看器,则可以将unicode字符直接删除到代码中。 There's a set of characters in there called "Digits" which has all the superscript and subscript number characters. 那里有一组叫做“数字”的字符,里面有所有的上标和下标数字。 Just leave your cursor at the appropriate spot in your code window and double-click in the character viewer to insert the character you want. 只需将光标留在代码窗口中的适当位置,然后双击字符查看器即可插入所需的字符。

With the right font, you could probably do this with any letter as well, but the character map only has a handful of non-numbers available for this that I've seen. 使用正确的字体,您也可以使用任何字母执行此操作,但字符映射只有少量可用的非数字,我已经看过。

Alternatively you can just put the unicode characters in your source content, but in a lot of cases (like mine), that isn't possible. 或者,您可以将unicode字符放在源内容中,但在很多情况下(如我的),这是不可能的。

Swift 4 斯威夫特4

Very loosely based off of Graham Perks' answer. 基于Graham Perks的回答非常宽松。 I could not make his code work as is but after three hours of work I've created something that works great! 我无法使他的代码工作,但经过三个小时的工作,我创造了一些非常好的东西! If you'd prefer a full implementation of this along with a bunch of nifty other performance and feature add-ons (links, async drawing, etc), check out my single file library DYLabel . 如果你更喜欢这个的完整实现以及一堆漂亮的其他性能和功能附加组件(链接,异步绘图等),请查看我的单个文件库DYLabel If not, read on. 如果没有,请继续阅读。

I explain everything I'm doing in the comments. 我在评论中解释我正在做的一切。 This is the draw method, to be called from drawRect: 这是draw方法,从drawRect调用:

/// Draw text on a given context. Supports superscript using NSBaselineOffsetAttributeName
///
/// This method works by drawing the text backwards (i.e. last line first). This is very very important because it's how we ensure superscripts don't overlap the text above it. In other words, we need to start from the bottom, get the height of the text we just drew, and then draw the next text above it. This could be done in a forward direction but you'd have to use lookahead which IMO is more work.
///
/// If you have to modify on this, remember that CT uses a mathmatical origin (i.e. 0,0 is bottom left like a cartisian plane)
/// - Parameters:
///   - context: A core graphics draw context
///   - attributedText: An attributed string
func drawText(context:CGContext, attributedText: NSAttributedString) {
    //Create our CT boiler plate
    let framesetter = CTFramesetterCreateWithAttributedString(attributedText)
    let textRect = bounds
    let path = CGPath(rect: textRect, transform: nil)
    let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)

    //Fetch our lines, bridging to swift from CFArray
    let lines = CTFrameGetLines(frame) as [AnyObject]
    let lineCount = lines.count

    //Get the line origin coordinates. These are used for calculating stock line height (w/o baseline modifications)
    var lineOrigins = [CGPoint](repeating: CGPoint.zero, count: lineCount)
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &lineOrigins);

    //Since we're starting from the bottom of the container we need get our bottom offset/padding (so text isn't slammed to the bottom or cut off)
    var ascent:CGFloat = 0
    var descent:CGFloat = 0
    var leading:CGFloat = 0
    if lineCount > 0 {
        CTLineGetTypographicBounds(lines.last as! CTLine, &ascent, &descent, &leading)
    }

    //This variable holds the current draw position, relative to CT origin of the bottom left
    //https://stackoverflow.com/a/27631737/1166266
    var drawYPositionFromOrigin:CGFloat = descent

    //Again, draw the lines in reverse so we don't need look ahead
    for lineIndex in (0..<lineCount).reversed()  {
        //Calculate the current line height so we can accurately move the position up later
        let lastLinePosition = lineIndex > 0 ? lineOrigins[lineIndex - 1].y: textRect.height
        let currentLineHeight = lastLinePosition - lineOrigins[lineIndex].y
        //Throughout the loop below this variable will be updated to the tallest value for the current line
        var maxLineHeight:CGFloat = currentLineHeight

        //Grab the current run glyph. This is used for attributed string interop
        let glyphRuns = CTLineGetGlyphRuns(lines[lineIndex] as! CTLine) as [AnyObject]

        for run in glyphRuns {
            let run = run as! CTRun
            //Convert the format range to something we can match to our string
            let runRange = CTRunGetStringRange(run)

            let attribuetsAtPosition = attributedText.attributes(at: runRange.location, effectiveRange: nil)
            var baselineAdjustment: CGFloat = 0.0
            if let adjust = attribuetsAtPosition[NSAttributedStringKey.baselineOffset] as? NSNumber {
                //We have a baseline offset!
                baselineAdjustment = CGFloat(adjust.floatValue)
            }

            //Check if this glyph run is tallest, and move it if it is
            maxLineHeight = max(currentLineHeight + baselineAdjustment, maxLineHeight)

            //Move the draw head. Note that we're drawing from the unupdated drawYPositionFromOrigin. This is again thanks to CT cartisian plane where we draw from the bottom left of text too.
            context.textPosition = CGPoint.init(x: lineOrigins[lineIndex].x, y: drawYPositionFromOrigin)
            //Draw!
            CTRunDraw(run, context, CFRangeMake(0, 0))

        }
        //Move our position because we've completed the drawing of the line which is at most `maxLineHeight`
        drawYPositionFromOrigin += maxLineHeight
    }
}

I also made a method which calculates the required height of the text given a width. 我还制作了一个方法来计算给定宽度的文本所需的高度。 It's exactly the same code except it doesn't draw anything. 这是完全相同的代码,除了它没有绘制任何东西。

/// Calculate the height if it were drawn using `drawText`
/// Uses the same code as drawText except it doesn't draw.
///
/// - Parameters:
///   - attributedText: The text to calculate the height of
///   - width: The constraining width
///   - estimationHeight: Optional paramater, default 30,000px. This is the container height used to layout the text. DO NOT USE CGFLOATMAX AS IT CORE TEXT CANNOT CREATE A FRAME OF THAT SIZE.
/// - Returns: The size required to fit the text
static func size(of attributedText:NSAttributedString,width:CGFloat, estimationHeight:CGFloat?=30000) -> CGSize {
    let framesetter = CTFramesetterCreateWithAttributedString(attributedText)
    let textRect = CGRect.init(x: 0, y: 0, width: width, height: estimationHeight!)
    let path = CGPath(rect: textRect, transform: nil)
    let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)

    //Fetch our lines, bridging to swift from CFArray
    let lines = CTFrameGetLines(frame) as [AnyObject]
    let lineCount = lines.count

    //Get the line origin coordinates. These are used for calculating stock line height (w/o baseline modifications)
    var lineOrigins = [CGPoint](repeating: CGPoint.zero, count: lineCount)
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &lineOrigins);

    //Since we're starting from the bottom of the container we need get our bottom offset/padding (so text isn't slammed to the bottom or cut off)
    var ascent:CGFloat = 0
    var descent:CGFloat = 0
    var leading:CGFloat = 0
    if lineCount > 0 {
        CTLineGetTypographicBounds(lines.last as! CTLine, &ascent, &descent, &leading)
    }

    //This variable holds the current draw position, relative to CT origin of the bottom left
    var drawYPositionFromOrigin:CGFloat = descent

    //Again, draw the lines in reverse so we don't need look ahead
    for lineIndex in (0..<lineCount).reversed()  {
        //Calculate the current line height so we can accurately move the position up later
        let lastLinePosition = lineIndex > 0 ? lineOrigins[lineIndex - 1].y: textRect.height
        let currentLineHeight = lastLinePosition - lineOrigins[lineIndex].y
        //Throughout the loop below this variable will be updated to the tallest value for the current line
        var maxLineHeight:CGFloat = currentLineHeight

        //Grab the current run glyph. This is used for attributed string interop
        let glyphRuns = CTLineGetGlyphRuns(lines[lineIndex] as! CTLine) as [AnyObject]

        for run in glyphRuns {
            let run = run as! CTRun
            //Convert the format range to something we can match to our string
            let runRange = CTRunGetStringRange(run)

            let attribuetsAtPosition = attributedText.attributes(at: runRange.location, effectiveRange: nil)
            var baselineAdjustment: CGFloat = 0.0
            if let adjust = attribuetsAtPosition[NSAttributedStringKey.baselineOffset] as? NSNumber {
                //We have a baseline offset!
                baselineAdjustment = CGFloat(adjust.floatValue)
            }

            //Check if this glyph run is tallest, and move it if it is
            maxLineHeight = max(currentLineHeight + baselineAdjustment, maxLineHeight)

            //Skip drawing since this is a height calculation
        }
        //Move our position because we've completed the drawing of the line which is at most `maxLineHeight`
        drawYPositionFromOrigin += maxLineHeight
    }
    return CGSize.init(width: width, height: drawYPositionFromOrigin)
}

Like everything I write, I also did some benchmarks against some public libraries and system functions (even though they won't work here). 就像我写的一切一样,我也对一些公共图书馆和系统功能做了一些基准测试(尽管它们在这里不起作用)。 I used a huge, complex string here to keep anyone from taking unfair shortcuts. 我在这里使用了一个巨大而复杂的字符串,以阻止任何人使用不公平的快捷方式。

---HEIGHT CALCULATION---
Runtime for 1000 iterations (ms) BoundsForRect: 5415.030002593994
Runtime for 1000 iterations (ms) layoutManager: 5370.990991592407
Runtime for 1000 iterations (ms) CTFramesetterSuggestFrameSizeWithConstraints: 2372.151017189026
Runtime for 1000 iterations (ms) CTFramesetterCreateFrame ObjC: 2300.302028656006
Runtime for 1000 iterations (ms) CTFramesetterCreateFrame-Swift: 2313.6669397354126
Runtime for 1000 iterations (ms) THIS ANSWER size(of:): 2566.351056098938


---RENDER---
Runtime for 1000 iterations (ms) AttributedLabel: 35.032033920288086
Runtime for 1000 iterations (ms) UILabel: 45.948028564453125
Runtime for 1000 iterations (ms) TTTAttributedLabel: 301.1329174041748
Runtime for 1000 iterations (ms) THIS ANSWER: 20.398974418640137

So summary time: we did very well! 总结时间:我们做得很好! size(of...) is nearly equal to stock CT layout which means that our addon for superscript is fairly cheap despite using a hash table lookup. size(of...)几乎等于库存CT布局,这意味着尽管使用哈希表查找,我们的上标也相当便宜。 We do, however, flat out win on draw calls. 但是,我们确实在平局电话中获胜。 I suspect that this is due to the very expensive 30k pixel estimation frame we have to create. 我怀疑这是由于我们必须创建的非常昂贵的30k像素估计帧。 If we make a better estimate performance will be better. 如果我们做出更好的估计,性能会更好。 I've already been working for about three hours so I'm calling it quits and leaving that as an exercise to the reader. 我已经工作了大约三个小时,所以我称之为退出并将其作为练习留给读者。

I struggled with this problem as well. 我也在努力解决这个问题。 It turns out, as some of the posters above suggested, that none of the fonts that come with IOS support superscripting or subscripting. 事实证明,正如上面提到的一些海报所示,IOS附带的任何字体都不支持上标或下标。 My solution was to purchase and install two custom superscript and subscript fonts (They were $9.99 each and here's a link to the site http://superscriptfont.com/ ). 我的解决方案是购买和安装两个自定义上标和下标字体(每个9.99美元,这里是链接到网站http://superscriptfont.com/ )。

Not really that hard to do. 真的很难做到。 Just add the font files as resources and add info.plist entries for "Font provided by application". 只需将字体文件添加为资源,并为“应用程序提供的字体”添加info.plist条目。

The next step was to search for the appropriate tags in my NSAttributedString, remove the tags and apply the font to the text. 下一步是在我的NSAttributedString中搜索相应的标记,删除标记并将字体应用于文本。

Works great! 效果很好!

A Swift 2 twist on Dimitri's answer; Dimitri答案的Swift 2扭曲; effectively implements NSBaselineOffsetAttributeName. 有效地实现了NSBaselineOffsetAttributeName。

When coding I was in a UIView so had a reasonable bounds rect to use. 编码时,我在UIView中,因此有一个合理的界限使用。 His answer calculated its own rect. 他的答案计算了自己的矩形。

func drawText(context context:CGContextRef, attributedText: NSAttributedString) {

    // All this CoreText iteration just to add support for superscripting.
    // NSBaselineOffsetAttributeName isn't supported by CoreText. So we manully iterate through 
    // all the text ranges, rendering each, and offsetting the baseline where needed.

    let framesetter = CTFramesetterCreateWithAttributedString(attributedText)
    let textRect = CGRectOffset(bounds, 0, 0)
    let path = CGPathCreateWithRect(textRect, nil)
    let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)

    // All the lines of text we'll render...
    let lines = CTFrameGetLines(frame) as [AnyObject]
    let lineCount = lines.count

    // And their origin coordinates...
    var lineOrigins = [CGPoint](count: lineCount, repeatedValue: CGPointZero)
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &lineOrigins);

    for lineIndex in 0..<lineCount  {
        let lineObject = lines[lineIndex]

        // Each run of glyphs we'll render...
        let glyphRuns = CTLineGetGlyphRuns(lineObject as! CTLine) as [AnyObject]
        for r in glyphRuns {
            let run = r as! CTRun
            let runRange = CTRunGetStringRange(run)

            // What attributes are in the NSAttributedString here? If we find NSBaselineOffsetAttributeName, 
            // adjust the baseline.
            let attrs = attributedText.attributesAtIndex(runRange.location, effectiveRange: nil)
            var baselineAdjustment: CGFloat = 0.0
            if let adjust = attrs[NSBaselineOffsetAttributeName as String] as? NSNumber {
                baselineAdjustment = CGFloat(adjust.floatValue)
            }

            CGContextSetTextPosition(context, lineOrigins[lineIndex].x, lineOrigins[lineIndex].y - 25 + baselineAdjustment)

            CTRunDraw(run, context, CFRangeMake(0, 0))
        }
    }
}

With IOS 11 Apple introduced a new string attribute name: kCTBaselineOffsetAttributeName which works with Core Text.在 IOS 11 中,Apple 引入了一个新的字符串属性名称: kCTBaselineOffsetAttributeName ,它适用于 Core Text。 Note that the offset direction is different from NSBaselineOffsetAttributeName used with NSAttributedStrings on UILabels etc (a positive offset moves the baseline downwards).请注意,偏移方向与 NSBaselineOffsetAttributeName 在 UILabels 等上与 NSAttributedStrings NSBaselineOffsetAttributeName使用不同(正偏移使基线向下移动)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM