MLLabelLayoutManager.m 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. //
  2. // MLLabelLayoutManager.m
  3. // MLLabel
  4. //
  5. // Created by molon on 15/6/11.
  6. // Copyright (c) 2015年 molon. All rights reserved.
  7. //
  8. #import "MLLabelLayoutManager.h"
  9. @interface MLLabelLayoutManager()
  10. @property (nonatomic, assign) CGPoint lastDrawPoint;
  11. @end
  12. @implementation MLLabelLayoutManager
  13. - (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin
  14. {
  15. self.lastDrawPoint = origin;
  16. [super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin];
  17. self.lastDrawPoint = CGPointZero;
  18. }
  19. - (void)fillBackgroundRectArray:(const CGRect *)rectArray count:(NSUInteger)rectCount forCharacterRange:(NSRange)charRange color:(UIColor *)color
  20. {
  21. CGContextRef ctx = UIGraphicsGetCurrentContext();
  22. if (!ctx) {
  23. [super fillBackgroundRectArray:rectArray count:rectCount forCharacterRange:charRange color:color];
  24. return;
  25. }
  26. CGContextSaveGState(ctx);
  27. {
  28. [color setFill];
  29. NSRange glyphRange = [self glyphRangeForCharacterRange:charRange actualCharacterRange:NULL];
  30. CGPoint textOffset = self.lastDrawPoint;
  31. NSRange lineRange = NSMakeRange(glyphRange.location, 1);
  32. while (NSMaxRange(lineRange)<=NSMaxRange(glyphRange)) {
  33. //这里可以防止这行没有用到的区域也绘制上背景色,例如收到word wrap,center alignment影响后每行文字没有占满时候
  34. CGRect lineBounds = [self lineFragmentUsedRectForGlyphAtIndex:lineRange.location effectiveRange:&lineRange];
  35. lineBounds.origin.x += textOffset.x;
  36. lineBounds.origin.y += textOffset.y;
  37. //找到这行具有背景色文字区域的位置
  38. NSRange glyphRangeInLine = NSIntersectionRange(glyphRange,lineRange);
  39. NSRange truncatedGlyphRange = [self truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphRangeInLine.location];
  40. if (truncatedGlyphRange.location!=NSNotFound) {
  41. //这里的glyphRangeInLine本身可能会带有被省略的区间,而我们下面计算最大行高和最小drawY的实现是不需要考虑省略的区间的,否则也可能计算有误。所以这里我们给过滤掉
  42. NSRange sameRange = NSIntersectionRange(glyphRangeInLine, truncatedGlyphRange);
  43. if (sameRange.length>0&&NSMaxRange(sameRange)==NSMaxRange(glyphRangeInLine)) {
  44. //我们这里先只处理tail模式的
  45. //而经过测试truncatedGlyphRangeInLineFragmentForGlyphAtIndex暂时只支持NSLineBreakByTruncatingTail模式
  46. //其他两种暂时也不会用,即使用,现在通过TextKit的话也没法获取
  47. glyphRangeInLine = NSMakeRange(glyphRangeInLine.location, sameRange.location-glyphRangeInLine.location);
  48. }
  49. }
  50. if (glyphRangeInLine.length>0) {
  51. CGFloat startDrawY = CGFLOAT_MAX;
  52. CGFloat maxLineHeight = 0.0f; //找到这行的 背景色区间 的文字的最小Y值和最大的文字高度
  53. for (NSInteger glyphIndex = glyphRangeInLine.location; glyphIndex<NSMaxRange(glyphRangeInLine); glyphIndex++) {
  54. NSInteger charIndex = [self characterIndexForGlyphAtIndex:glyphIndex];
  55. UIFont *font = [self.textStorage attribute:NSFontAttributeName
  56. atIndex:charIndex
  57. effectiveRange:nil];
  58. //找到这个字的绘制位置
  59. CGPoint location = [self locationForGlyphAtIndex:glyphIndex];
  60. startDrawY = fmin(startDrawY, lineBounds.origin.y+location.y-font.ascender);
  61. // NSLog(@"char:%@ lineHeight:%lf",[self.textStorage.string substringWithRange:[self.textStorage.string rangeOfComposedCharacterSequenceAtIndex:charIndex]],font.lineHeight);
  62. maxLineHeight = fmax(maxLineHeight, font.lineHeight);
  63. }
  64. CGSize size = lineBounds.size;
  65. CGPoint orgin = lineBounds.origin;
  66. //调整下高度和绘制y值,这样做的目的是为了不会收到lineHeightMultiple和lineSpcing的影响,引起背景色绘制过高不工整
  67. orgin.y = startDrawY;
  68. size.height = maxLineHeight;
  69. lineBounds.size = size;
  70. lineBounds.origin = orgin;
  71. }
  72. for (NSInteger i=0; i<rectCount; i++) {
  73. //找到相交的区域并且绘制
  74. CGRect validRect = CGRectIntersection(lineBounds, rectArray[i]);
  75. if (!CGRectIsEmpty(validRect)) {
  76. CGContextFillRect(ctx, validRect);
  77. }
  78. }
  79. lineRange = NSMakeRange(NSMaxRange(lineRange), 1);
  80. }
  81. }
  82. CGContextRestoreGState(ctx);
  83. }
  84. @end