Giving Framesetter the correct line spacing adjustment

后端 未结 2 1437
隐瞒了意图╮
隐瞒了意图╮ 2021-01-03 11:27

Several posts have noted difficulties with getting an exact height out of CTFramesetterSuggestFrameSizeWithConstraints, and here, (framesetter post), @Chris DeSalvo gives wh

相关标签:
2条回答
  • 2021-01-03 11:58

    I too ran into this and here is the code that worked in a real project:

    // When you create an attributed string the default paragraph style has a leading 
    // of 0.0. Create a paragraph style that will set the line adjustment equal to
    // the leading value of the font. This logic will ensure that the measured
    // height for a given paragraph of attributed text will be accurate wrt the font.
    
    - (void) applyParagraphAttributes:(CFMutableAttributedStringRef)mAttributedString
    {
      CGFloat leading = CTFontGetLeading(self.plainTextFont);
    
      CTParagraphStyleSetting paragraphSettings[1] = {
        kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &leading
      };
    
      CTParagraphStyleRef  paragraphStyle = CTParagraphStyleCreate(paragraphSettings, 1);
    
      CFRange textRange = CFRangeMake(0, [self length]);
    
      CFStringRef keys[] = { kCTParagraphStyleAttributeName };
      CFTypeRef values[] = { paragraphStyle };
    
      CFDictionaryRef attrValues = CFDictionaryCreate(kCFAllocatorDefault,
                                                      (const void**)&keys,
                                                      (const void**)&values,
                                                      sizeof(keys) / sizeof(keys[0]),
                                                      &kCFTypeDictionaryKeyCallBacks,
                                                      &kCFTypeDictionaryValueCallBacks);
    
      BOOL clearOtherAttributes = FALSE;
      CFAttributedStringSetAttributes(mAttributedString, textRange, attrValues, (Boolean)clearOtherAttributes);
      CFRelease(attrValues);
    
      CFRelease(paragraphStyle);
    
      self.stringRange = textRange;
    
      return;
    }
    
    0 讨论(0)
  • 2021-01-03 12:04

    Answers to the 3 questions I had above:

    1. Yes, “leading” really what is meant by kCTParagraphStyleSpecifierLineSpacingAdjustment. Or at any rate, it works as expected.
    2. Use CTFontGetLeading(fontRef) to get the font's normal leading, or plug in whatever value (as a CGFloat) you choose.
    3. N/A.

    Answers 1 and 2 work: Specifying a leading value in a paragraphStyle attribute of your attributed string will enable the Core-Text framesetter to calculate its height exactly.

    There are two caveats:

    1. If you try to calculate heights incrementally, one string at a time, each string containing an initial line break, framesetter will consider that line break to represent an entire line, not just the leading. If you want the height of the concatenated strings, you have to feed that concatenation to the framesetter. Of course, you could keep track of the incremental height differences, but there's no way to avoid having framesetter recalculate the earlier string dimensions.
    2. CATextLayer ignores spacing adjustments (and other attributes). If framing per exact string height is an issue, you must draw direct to a CALayer.

    And there is one mystery: What is going on with UIFont's deprecated leading? Leading and lineHeight are two distinct things.

    0 讨论(0)
提交回复
热议问题