线上所有马甲包模板,与《猎豆》同UI。域名zhuadd

MLLabel.m 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. //
  2. // MLLabel.m
  3. // MLLabel
  4. //
  5. // Created by molon on 15/5/18.
  6. // Copyright (c) 2015年 molon. All rights reserved.
  7. //
  8. #import "MLLabel.h"
  9. #import "NSMutableAttributedString+MLLabel.h"
  10. #import "MLLabelLayoutManager.h"
  11. #import "NSString+MLLabel.h"
  12. #define kAdjustFontSizeEveryScalingFactor (M_E / M_PI)
  13. //总得有个极限
  14. static CGFloat MLFLOAT_MAX = 100000.0f;
  15. static CGFloat ADJUST_MIN_FONT_SIZE = 1.0f;
  16. static CGFloat ADJUST_MIN_SCALE_FACTOR = 0.01f;
  17. static NSArray * kStylePropertyNames() {
  18. static NSArray *_stylePropertyNames = nil;
  19. static dispatch_once_t onceToken;
  20. dispatch_once(&onceToken, ^{
  21. //TODO: 这个highlighted在tableview滚动到的时候会设置下(即使cell的selectStyle为None),然后就造成resetText,很鸡巴,耗费性能,这个似乎没辙,实在有必要,后期+个属性来开关
  22. _stylePropertyNames = @[@"font",@"textAlignment",@"textColor",@"highlighted",
  23. @"highlightedTextColor",@"shadowColor",@"shadowOffset",@"enabled",@"lineHeightMultiple",@"lineSpacing"];
  24. });
  25. return _stylePropertyNames;
  26. }
  27. @interface MLLabelStylePropertyRecord : NSObject
  28. @property (nonatomic, strong) UIFont *font;
  29. @property (nonatomic, assign) NSTextAlignment textAlignment;
  30. @property (nonatomic, strong) UIColor *textColor;
  31. @property (nonatomic, assign) BOOL highlighted;
  32. @property (nonatomic, strong) UIColor *highlightedTextColor;
  33. @property (nonatomic, strong) UIColor *shadowColor;
  34. @property (nonatomic, assign) CGSize shadowOffset;
  35. @property (nonatomic, assign) BOOL enabled;
  36. @property (nonatomic, assign) CGFloat lineHeightMultiple; //行高的multiple
  37. @property (nonatomic, assign) CGFloat lineSpacing; //行间距
  38. @end
  39. @implementation MLLabelStylePropertyRecord
  40. @end
  41. @interface MLLabel()<NSLayoutManagerDelegate>
  42. @property (nonatomic, strong) NSTextStorage *textStorage;
  43. @property (nonatomic, strong) MLLabelLayoutManager *layoutManager;
  44. @property (nonatomic, strong) NSTextContainer *textContainer;
  45. @property (nonatomic, assign) MLLastTextType lastTextType;
  46. @property (nonatomic, strong) MLLabelStylePropertyRecord *styleRecord;
  47. //为什么需要这个,是因为setAttributedText之后内部可能会对其进行了改动,然后例如再次更新style属性,然后更新绘制会出问题。索性都以记录的最原始的为准。
  48. @property (nonatomic, copy) NSAttributedString *lastAttributedText;
  49. //读取的时候需要
  50. @property (nonatomic, copy) NSString *lastText;
  51. @end
  52. @implementation MLLabel
  53. #pragma mark - init
  54. - (instancetype)initWithFrame:(CGRect)frame
  55. {
  56. self = [super initWithFrame:frame];
  57. if (self) {
  58. [self commonInit];
  59. }
  60. return self;
  61. }
  62. - (id)initWithCoder:(NSCoder *)aDecoder
  63. {
  64. self = [super initWithCoder:aDecoder];
  65. if (self)
  66. {
  67. [self commonInit];
  68. }
  69. return self;
  70. }
  71. - (void)commonInit
  72. {
  73. self.lineHeightMultiple = 1.0f;
  74. //设置TextKit初始相关
  75. [self.textStorage addLayoutManager:self.layoutManager];
  76. [self.layoutManager addTextContainer:self.textContainer];
  77. //label helper相关
  78. if ([super attributedText]) {
  79. self.attributedText = [super attributedText];
  80. }else{
  81. self.text = [super text];
  82. }
  83. //kvo 监视style属性
  84. for (NSString *key in kStylePropertyNames()) {
  85. [self.styleRecord setValue:[self valueForKey:key] forKey:key];
  86. //不直接使用NSKeyValueObservingOptionInitial来初始化赋值record,是防止无用的resettext
  87. [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
  88. }
  89. }
  90. - (void)dealloc
  91. {
  92. //kvo 移除监视style属性
  93. for (NSString *key in kStylePropertyNames()) {
  94. [self removeObserver:self forKeyPath:key context:nil];
  95. }
  96. }
  97. #pragma mark - KVO
  98. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  99. {
  100. if ([kStylePropertyNames() containsObject:keyPath]) {
  101. //存到记录对象里
  102. [_styleRecord setValue:[object valueForKey:keyPath] forKey:keyPath];
  103. id old = change[NSKeyValueChangeOldKey];
  104. id new = change[NSKeyValueChangeNewKey];
  105. if ([old isEqual:new]||(!old&&!new)) {
  106. return;
  107. }
  108. [self reSetText];
  109. }else{
  110. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  111. }
  112. }
  113. #pragma mark - getter
  114. - (NSTextStorage *)textStorage
  115. {
  116. if (!_textStorage) {
  117. _textStorage = [NSTextStorage new];
  118. }
  119. return _textStorage;
  120. }
  121. - (MLLabelLayoutManager *)layoutManager
  122. {
  123. if (!_layoutManager) {
  124. _layoutManager = [MLLabelLayoutManager new];
  125. _layoutManager.allowsNonContiguousLayout = NO;
  126. _layoutManager.delegate = self;
  127. }
  128. return _layoutManager;
  129. }
  130. - (NSTextContainer *)textContainer
  131. {
  132. if (!_textContainer) {
  133. _textContainer = [NSTextContainer new];
  134. _textContainer.maximumNumberOfLines = self.numberOfLines;
  135. _textContainer.lineBreakMode = self.lineBreakMode;
  136. _textContainer.lineFragmentPadding = 0.0f;
  137. _textContainer.size = self.frame.size;
  138. }
  139. return _textContainer;
  140. }
  141. - (MLLabelStylePropertyRecord *)styleRecord
  142. {
  143. if (!_styleRecord) {
  144. _styleRecord = [MLLabelStylePropertyRecord new];
  145. }
  146. return _styleRecord;
  147. }
  148. - (NSAttributedString *)attributedText
  149. {
  150. return _lastTextType==MLLastTextTypeAttributed?_lastAttributedText:[self attributedTextForTextStorageFromLabelProperties];
  151. }
  152. - (NSString*)text
  153. {
  154. return _lastTextType==MLLastTextTypeAttributed?_lastAttributedText.string:_lastText;
  155. }
  156. #pragma mark - set text
  157. - (void)setLastTextType:(MLLastTextType)lastTextType
  158. {
  159. _lastTextType = lastTextType;
  160. //重置下
  161. self.lastText = nil;
  162. self.lastAttributedText = nil;
  163. }
  164. - (void)setText:(NSString *)text
  165. {
  166. NSAssert(!text||[text isKindOfClass:[NSString class]], @"text must be NSString");
  167. self.lastTextType = MLLastTextTypeNormal;
  168. self.lastText = text;
  169. [self invalidateIntrinsicContentSize];
  170. // [super setText:text];
  171. [_textStorage setAttributedString:[self attributedTextForTextStorageFromLabelProperties]];
  172. //如果text和原本的一样的话 super 是不会触发redraw的,但是很遗憾我们的label比较灵活,验证起来很麻烦,所以还是都重绘吧
  173. [self setNeedsDisplay];
  174. }
  175. - (void)setAttributedText:(NSAttributedString *)attributedText
  176. {
  177. NSAssert(!attributedText||[attributedText isKindOfClass:[NSAttributedString class]], @"text must be NSAttributedString");
  178. self.lastTextType = MLLastTextTypeAttributed;
  179. self.lastAttributedText = attributedText;
  180. [self invalidateIntrinsicContentSize];
  181. // [super setAttributedText:attributedText];
  182. [_textStorage setAttributedString:[self attributedTextForTextStorageFromLabelProperties]];
  183. //如果text和原本的一样的话 super 是不会触发redraw的,但是很遗憾我们的label比较灵活,验证起来很麻烦,所以还是都重绘吧
  184. [self setNeedsDisplay];
  185. // NSLog(@"set attr text %p",self);
  186. }
  187. #pragma mark - common helper
  188. - (CGSize)textContainerSizeWithBoundsSize:(CGSize)size
  189. {
  190. //bounds改了之后,要相应的改变textContainer的size,但是要根据insets调整
  191. CGFloat width = fmax(0, size.width-_textInsets.left-_textInsets.right);
  192. CGFloat height = fmax(0, size.height-_textInsets.top-_textInsets.bottom);
  193. return CGSizeMake(width, height);
  194. }
  195. - (void)reSetText
  196. {
  197. if (_lastTextType == MLLastTextTypeNormal) {
  198. self.text = _lastText;
  199. }else{
  200. self.attributedText = _lastAttributedText;
  201. }
  202. }
  203. /**
  204. * 根据label的属性来进行处理并返回给textStorage使用的
  205. */
  206. - (NSMutableAttributedString*)attributedTextForTextStorageFromLabelProperties
  207. {
  208. if (_lastTextType==MLLastTextTypeNormal) {
  209. if (!_lastText) {
  210. return [[NSMutableAttributedString alloc]initWithString:@""];
  211. }
  212. //根据text和label默认的一些属性得到attributedText
  213. return [[NSMutableAttributedString alloc] initWithString:_lastText attributes:[self attributesFromLabelProperties]];
  214. }
  215. if (!_lastAttributedText) {
  216. return [[NSMutableAttributedString alloc]initWithString:@""];
  217. }
  218. //遍历并且添加Label默认的属性
  219. NSMutableAttributedString *newAttrStr = [[NSMutableAttributedString alloc]initWithString:_lastAttributedText.string attributes:[self attributesFromLabelProperties]];
  220. [_lastAttributedText enumerateAttributesInRange:NSMakeRange(0, newAttrStr.length) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
  221. if (attrs.count>0) {
  222. // [newAttrStr removeAttributes:[attrs allKeys] range:range];
  223. [newAttrStr addAttributes:attrs range:range];
  224. }
  225. }];
  226. return newAttrStr;
  227. }
  228. - (NSDictionary *)attributesFromLabelProperties
  229. {
  230. //颜色
  231. UIColor *color = self.styleRecord.textColor;
  232. if (!_styleRecord.enabled)
  233. {
  234. color = [UIColor lightGrayColor];
  235. }
  236. else if (_styleRecord.highlighted)
  237. {
  238. color = _styleRecord.highlightedTextColor;
  239. }
  240. if (!color) {
  241. color = _styleRecord.textColor;
  242. if (!color) {
  243. color = [UIColor darkTextColor];
  244. }
  245. }
  246. //阴影
  247. NSShadow *shadow = shadow = [[NSShadow alloc] init];
  248. if (_styleRecord.shadowColor)
  249. {
  250. shadow.shadowColor = _styleRecord.shadowColor;
  251. shadow.shadowOffset = _styleRecord.shadowOffset;
  252. }
  253. else
  254. {
  255. shadow.shadowOffset = CGSizeMake(0, -1);
  256. shadow.shadowColor = nil;
  257. }
  258. //水平位置
  259. NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
  260. paragraph.alignment = _styleRecord.textAlignment;
  261. paragraph.lineSpacing = _styleRecord.lineSpacing;
  262. paragraph.lineHeightMultiple = _styleRecord.lineHeightMultiple;
  263. if (!_styleRecord.font) {
  264. _styleRecord.font = [UIFont systemFontOfSize:17.0f];
  265. }
  266. //最终
  267. NSDictionary *attributes = @{NSFontAttributeName : _styleRecord.font,
  268. NSForegroundColorAttributeName : color,
  269. NSShadowAttributeName : shadow,
  270. NSParagraphStyleAttributeName : paragraph,
  271. };
  272. return attributes;
  273. }
  274. - (CGRect)textRectForBounds:(CGRect)bounds attributedString:(NSAttributedString*)attributedString limitedToNumberOfLines:(NSInteger)numberOfLines lineCount:(NSInteger*)lineCount
  275. {
  276. //这种算是特殊情况,如果为空字符串,那就没必要必要了,也忽略textInset,这样比较合理
  277. if (attributedString.length<=0) {
  278. bounds.size = CGSizeZero;
  279. return bounds;
  280. }
  281. CGSize newTextContainerSize = [self textContainerSizeWithBoundsSize:bounds.size];
  282. if (newTextContainerSize.width<=0||newTextContainerSize.height<=0){
  283. CGRect textBounds = CGRectZero;
  284. textBounds.origin = bounds.origin;
  285. textBounds.size = CGSizeMake(fmin(_textInsets.left+_textInsets.right,CGRectGetWidth(bounds)), fmin(_textInsets.top+_textInsets.bottom,CGRectGetHeight(bounds)));
  286. return textBounds;
  287. }
  288. CGRect textBounds = CGRectZero;
  289. @autoreleasepool {
  290. CGSize savedTextContainerSize = _textContainer.size;
  291. NSInteger savedTextContainerNumberOfLines = _textContainer.maximumNumberOfLines;
  292. _textContainer.size = newTextContainerSize;
  293. _textContainer.maximumNumberOfLines = numberOfLines;
  294. NSAttributedString *savedAttributedString = nil;
  295. if (![_textStorage isEqual:attributedString]) {
  296. savedAttributedString = [_textStorage copy];
  297. [_textStorage setAttributedString:attributedString];
  298. }
  299. NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
  300. if (lineCount) {
  301. [_layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) {
  302. (*lineCount)++;
  303. }];
  304. //在最后字符为换行符的情况下,实际绘制出来的是会多那个一行,这里作为AppleBUG修正
  305. if ([_textStorage.string isNewlineCharacterAtEnd]) {
  306. (*lineCount)++;
  307. }
  308. }
  309. textBounds = [_layoutManager usedRectForTextContainer:_textContainer];
  310. //还原
  311. if (savedAttributedString) {
  312. [_textStorage setAttributedString:savedAttributedString];
  313. }
  314. _textContainer.size = savedTextContainerSize;
  315. _textContainer.maximumNumberOfLines = savedTextContainerNumberOfLines;
  316. }
  317. //最终修正
  318. textBounds.size.width = fmin(ceilf(textBounds.size.width), newTextContainerSize.width);
  319. textBounds.size.height = fmin(ceilf(textBounds.size.height), newTextContainerSize.height);
  320. textBounds.origin = bounds.origin;
  321. textBounds.size = CGSizeMake(fmin(CGRectGetWidth(textBounds)+_textInsets.left+_textInsets.right,CGRectGetWidth(bounds)), fmin(CGRectGetHeight(textBounds)+_textInsets.top+_textInsets.bottom,CGRectGetHeight(bounds)));
  322. // NSLog(@"bounds:%@ result:%@ %p",NSStringFromCGRect(bounds),NSStringFromCGRect(textBounds),self);
  323. return textBounds;
  324. }
  325. #pragma mark - draw
  326. - (BOOL)adjustsCurrentFontSizeToFitWidthWithScaleFactor:(CGFloat)scaleFactor numberOfLines:(NSInteger)numberOfLines originalAttributedText:(NSAttributedString*)originalAttributedText bounds:(CGRect)bounds resultAttributedString:(NSAttributedString**)resultAttributedString
  327. {
  328. __block BOOL mustReturnYES = NO;
  329. if (self.minimumScaleFactor > scaleFactor) {
  330. scaleFactor = self.minimumScaleFactor; //这个的话 就不能在循环验证了
  331. mustReturnYES = YES;
  332. }
  333. //总得有个极限
  334. scaleFactor = fmax(scaleFactor, ADJUST_MIN_SCALE_FACTOR);
  335. //遍历并且设置一个新的字体
  336. NSMutableAttributedString *attrStr = [originalAttributedText mutableCopy];
  337. if (scaleFactor!=1.0f) { //如果是1.0f的话就没有调整font size的必要
  338. [attrStr enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attrStr.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
  339. UIFont *font = (UIFont *)value;
  340. if (font&&[font isKindOfClass:[UIFont class]]) {
  341. NSString *fontName = font.fontName;
  342. CGFloat newSize = font.pointSize*scaleFactor;
  343. if (newSize<ADJUST_MIN_FONT_SIZE) { //字体的极限
  344. mustReturnYES = YES;
  345. }
  346. UIFont *newFont = [UIFont fontWithName:fontName size:newSize];
  347. // [attrStr removeAttribute:NSFontAttributeName range:range];
  348. [attrStr addAttribute:NSFontAttributeName value:newFont range:range];
  349. }
  350. }];
  351. }
  352. //返回是否需要继续调整字体大小
  353. if (mustReturnYES) {
  354. if (resultAttributedString) {
  355. (*resultAttributedString) = attrStr;
  356. }
  357. return YES;
  358. }
  359. CGSize currentTextSize = CGSizeZero;
  360. if (numberOfLines>0) {
  361. NSInteger lineCount = 0;
  362. currentTextSize = [self textRectForBounds:CGRectMake(0, 0, CGRectGetWidth(bounds), MLFLOAT_MAX) attributedString:attrStr limitedToNumberOfLines:0 lineCount:&lineCount].size;
  363. //如果求行数大于设置行数,也不认为塞满了
  364. if (lineCount>numberOfLines) {
  365. return NO;
  366. }
  367. }else{
  368. currentTextSize = [self textRectForBounds:CGRectMake(0, 0, CGRectGetWidth(bounds), MLFLOAT_MAX) attributedString:attrStr limitedToNumberOfLines:0 lineCount:NULL].size;
  369. }
  370. //大小已经足够就认作OK
  371. if (currentTextSize.width<=CGRectGetWidth(bounds)&&currentTextSize.height<=CGRectGetHeight(bounds)) {
  372. if (resultAttributedString) {
  373. (*resultAttributedString) = attrStr;
  374. }
  375. return YES;
  376. }
  377. return NO;
  378. }
  379. - (void)drawTextInRect:(CGRect)rect
  380. {
  381. // NSLog(@"draw text %p",self);
  382. //不调用super方法
  383. // [super drawTextInRect:rect]; //这里调用可以查看是否绘制和原来的不一致
  384. //如果绘制区域本身就为0,就应该直接返回,不做多余操作。
  385. if (_textContainer.size.width<=0||_textContainer.size.height<=0){
  386. return;
  387. }
  388. if (self.adjustsFontSizeToFitWidth) {
  389. //初始scale,每次adjust都需要从头开始,因为也可能有当前font被adjust小过需要还原。
  390. CGFloat scaleFactor = 1.0f;
  391. BOOL mustContinueAdjust = YES;
  392. NSAttributedString *attributedString = [self attributedTextForTextStorageFromLabelProperties];
  393. //numberOfLine>0时候可以直接尝试找寻一个preferredScale
  394. if (self.numberOfLines>0) {
  395. //一点点矫正,以使得内容能放到当前的size里
  396. //找到当前text绘制在一行时候需要占用的宽度,其实这个值很可能不够,因为多行时候可能会因为wordwrap的关系多行+起的总宽度会多。但是这个能找到一个合适的矫正过程的开始值,大大减少矫正次数。
  397. //还有一种情况就是,有可能由于字符串里带换行符的关系造成压根不可能绘制到一行,这时候应该取会显示的最长的那一行。所以这里需要先截除必然不会显示的部分。
  398. NSUInteger stringlineCount = [attributedString.string lineCount];
  399. if (stringlineCount>self.numberOfLines) {
  400. //这里说明必然要截取
  401. attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, [attributedString.string lengthToLineIndex:self.numberOfLines-1])];
  402. }
  403. CGFloat textWidth = [self textRectForBounds:CGRectMake(0, 0, MLFLOAT_MAX, MLFLOAT_MAX) attributedString:attributedString limitedToNumberOfLines:0 lineCount:NULL].size.width;
  404. textWidth = fmax(0, textWidth-_textInsets.left-_textInsets.right);
  405. if (textWidth>0) {
  406. CGFloat availableWidth = _textContainer.size.width*self.numberOfLines;
  407. if (textWidth > availableWidth) {
  408. //这里得到的scaleFactor肯定是大于这个的是必然不满足的,目的就是找这个,以能减少下面的矫正次数。
  409. scaleFactor = availableWidth / textWidth;
  410. }
  411. }else{
  412. mustContinueAdjust = NO;
  413. }
  414. }
  415. if (mustContinueAdjust) {
  416. //一点点矫正,以使得内容能放到当前的size里
  417. NSAttributedString *resultAttributedString = attributedString;
  418. while (![self adjustsCurrentFontSizeToFitWidthWithScaleFactor:scaleFactor numberOfLines:self.numberOfLines originalAttributedText:attributedString bounds:self.bounds resultAttributedString:&resultAttributedString]){
  419. scaleFactor *= kAdjustFontSizeEveryScalingFactor;
  420. };
  421. [_textStorage setAttributedString:resultAttributedString];
  422. }
  423. }
  424. CGPoint textOffset;
  425. //这里根据container的size和manager布局属性以及字符串来得到实际绘制的range区间
  426. NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
  427. //获取绘制区域大小
  428. CGRect drawBounds = [_layoutManager usedRectForTextContainer:_textContainer];
  429. //因为label是默认垂直居中的,所以需要根据实际绘制区域的bounds来调整出居中的offset
  430. textOffset = [self textOffsetWithTextSize:drawBounds.size];
  431. if (_doBeforeDrawingTextBlock) {
  432. //而实际上drawBounds的宽度可能不是我们想要的,我们想要的是_textContainer的宽度,但是高度需要是真实绘制高度
  433. CGSize drawSize = CGSizeMake(_textContainer.size.width, drawBounds.size.height);
  434. _doBeforeDrawingTextBlock(rect,textOffset,drawSize);
  435. }
  436. //绘制文字
  437. [_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textOffset];
  438. [_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textOffset];
  439. }
  440. //这个计算出来的是绘制起点
  441. - (CGPoint)textOffsetWithTextSize:(CGSize)textSize
  442. {
  443. CGPoint textOffset = CGPointZero;
  444. //根据insets和默认垂直居中来计算出偏移
  445. textOffset.x = _textInsets.left;
  446. CGFloat paddingHeight = (_textContainer.size.height - textSize.height) / 2.0f;
  447. textOffset.y = paddingHeight+_textInsets.top;
  448. return textOffset;
  449. }
  450. //- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager
  451. // shouldGenerateGlyphs:(const CGGlyph *)glyphs
  452. // properties:(const NSGlyphProperty *)props
  453. // characterIndexes:(const NSUInteger *)charIndexes
  454. // font:(UIFont *)aFont
  455. // forGlyphRange:(NSRange)glyphRange
  456. //{
  457. // NSLog(@"shouldGenerateGlyphs: start:%ld end:%ld",*charIndexes,charIndexes[glyphRange.length-1]);
  458. //// if (*charIndexes>=100) {
  459. //// return 0;
  460. //// }
  461. // [layoutManager setGlyphs:glyphs properties:props characterIndexes:charIndexes font:aFont forGlyphRange:glyphRange];
  462. // return glyphRange.length;
  463. //}
  464. #pragma mark - sizeThatsFit
  465. - (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
  466. {
  467. //fit实现与drawTextInRect大部分一个屌样,所以不写注释了。
  468. if (numberOfLines>1&&self.adjustsFontSizeToFitWidth) {
  469. CGFloat scaleFactor = 1.0f;
  470. NSAttributedString *attributedString = [self attributedTextForTextStorageFromLabelProperties];
  471. NSUInteger stringlineCount = [attributedString.string lineCount];
  472. if (stringlineCount>self.numberOfLines) {
  473. //这里说明必然要截取
  474. attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, [attributedString.string lengthToLineIndex:self.numberOfLines-1])];
  475. }
  476. CGFloat textWidth = [self textRectForBounds:CGRectMake(0, 0, MLFLOAT_MAX, MLFLOAT_MAX) attributedString:attributedString limitedToNumberOfLines:0 lineCount:NULL].size.width;
  477. textWidth = fmax(0, textWidth-_textInsets.left-_textInsets.right);
  478. if (textWidth>0) {
  479. CGFloat availableWidth = _textContainer.size.width*numberOfLines;
  480. if (textWidth > availableWidth) {
  481. //这里得到的scaleFactor肯定是大于这个的是必然不满足的,目的就是找这个,以能减少下面的矫正次数。
  482. scaleFactor = availableWidth / textWidth;
  483. }
  484. //一点点矫正,以使得内容能放到当前的size里
  485. NSAttributedString *resultAttributedString = attributedString;
  486. while (![self adjustsCurrentFontSizeToFitWidthWithScaleFactor:scaleFactor numberOfLines:numberOfLines originalAttributedText:attributedString bounds:bounds resultAttributedString:&resultAttributedString]){
  487. scaleFactor *= kAdjustFontSizeEveryScalingFactor;
  488. };
  489. //计算当前adjust之后的合适大小,为什么不用adjust里面的 因为也可能有异常情况,例如压根adjust就没走到计算大小那一步啊什么的。
  490. CGRect textBounds = [self textRectForBounds:bounds attributedString:resultAttributedString limitedToNumberOfLines:numberOfLines lineCount:NULL];
  491. return textBounds;
  492. }
  493. }
  494. return [self textRectForBounds:bounds attributedString:_textStorage limitedToNumberOfLines:numberOfLines lineCount:NULL];
  495. }
  496. - (CGSize)sizeThatFits:(CGSize)size
  497. {
  498. size = [super sizeThatFits:size];
  499. if (size.height>0) {
  500. size.height++;
  501. }
  502. return size;
  503. }
  504. - (CGSize)intrinsicContentSize {
  505. CGSize size = [super intrinsicContentSize];
  506. if (size.height>0) {
  507. size.height++;
  508. }
  509. return size;
  510. }
  511. - (CGSize)preferredSizeWithMaxWidth:(CGFloat)maxWidth
  512. {
  513. CGSize size = [self sizeThatFits:CGSizeMake(maxWidth, MLFLOAT_MAX)];
  514. size.width = fmin(size.width, maxWidth); //在numberOfLine为1模式下返回的可能会比maxWidth大,所以这里我们限制下
  515. return size;
  516. }
  517. #pragma mark - set 修改container size相关
  518. - (void)resizeTextContainerSize
  519. {
  520. if (_textContainer) {
  521. _textContainer.size = [self textContainerSizeWithBoundsSize:self.bounds.size];
  522. }
  523. }
  524. - (void)setFrame:(CGRect)frame
  525. {
  526. [super setFrame:frame];
  527. [self resizeTextContainerSize];
  528. }
  529. - (void)setBounds:(CGRect)bounds
  530. {
  531. [super setBounds:bounds];
  532. [self resizeTextContainerSize];
  533. }
  534. - (void)setTextInsets:(UIEdgeInsets)insets
  535. {
  536. _textInsets = insets;
  537. [self resizeTextContainerSize];
  538. [self invalidateIntrinsicContentSize];
  539. }
  540. #pragma mark - set container相关属性
  541. - (void)setNumberOfLines:(NSInteger)numberOfLines
  542. {
  543. BOOL isChanged = (numberOfLines!=_textContainer.maximumNumberOfLines);
  544. [super setNumberOfLines:numberOfLines];
  545. _textContainer.maximumNumberOfLines = numberOfLines;
  546. if (isChanged) {
  547. [self invalidateIntrinsicContentSize];
  548. [self setNeedsDisplay];
  549. }
  550. }
  551. - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode
  552. {
  553. [super setLineBreakMode:lineBreakMode];
  554. _textContainer.lineBreakMode = lineBreakMode;
  555. [self invalidateIntrinsicContentSize];
  556. }
  557. #pragma mark - set 其他
  558. - (void)setMinimumScaleFactor:(CGFloat)minimumScaleFactor
  559. {
  560. [super setMinimumScaleFactor:minimumScaleFactor];
  561. [self invalidateIntrinsicContentSize];
  562. [self setNeedsDisplay];
  563. }
  564. - (void)setDoBeforeDrawingTextBlock:(void (^)(CGRect rect,CGPoint beginOffset,CGSize drawSize))doBeforeDrawingTextBlock
  565. {
  566. _doBeforeDrawingTextBlock = doBeforeDrawingTextBlock;
  567. [self setNeedsDisplay];
  568. }
  569. #pragma mark - UIResponder
  570. - (BOOL)canBecomeFirstResponder {
  571. return YES;
  572. }
  573. - (BOOL)canPerformAction:(SEL)action
  574. withSender:(__unused id)sender
  575. {
  576. return (action == @selector(copy:));
  577. }
  578. #pragma mark - UIResponderStandardEditActions
  579. - (void)copy:(__unused id)sender {
  580. [[UIPasteboard generalPasteboard] setString:self.text];
  581. }
  582. @end