先上效果图
在做文字超出行数限制末尾添加高亮点击字的需求的时候,遇到的这么一个问题。
如果只是在末尾追加高亮点击字,直接用yytext 就可以了。
原本我也是直接用yytext处理掉。 yytext 效果如下:
好像完全满足,然而有一种情况,我们产品不想要这样。这段文字中间加了个换行,导致高亮字的那一行末尾是空的 的情况,yytext效果如下:
而我们的产品要的是这种情况也显示在最后。我用yytext没试出来。所以没的办法,只有自己想法子,最后就采用了下面我用的方式。
分为3个点
- 获取Label上每一行的文字
- 超出限制行数,将最大行文字做处理(如果增加了高亮字,但是高亮字+文字的width不够一行的width,进行补空格操作)
-
获取点击是否在高亮字上
首先通过CoreText里的CTFramesetter,CTFrame来获取每一行的文字,如下:
//获取每行文字
- (NSArray *)getSeparatedLinesWithWidth:(CGFloat)width
{
NSString *text = [self text];
if (!text || text.length<1) {
return 0;
}
UIFont *font = [self font];
CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
NSMutableArray *linesArray = [[NSMutableArray alloc]init];
for (id line in lines) {
CTLineRef lineRef = (__bridge CTLineRef )line;
CFRange lineRange = CTLineGetStringRange(lineRef);
NSRange range = NSMakeRange(lineRange.location, lineRange.length);
NSString *lineString = [text substringWithRange:range];
[linesArray addObject:lineString];
}
return linesArray;
}
然后根据获取到的每一行文字,做行数限制及补空格,高亮字的处理。
最后通过NSLayoutManager, NSTextContainer and NSTextStorage
来判断点击处是否在高亮字上,详细代码可看demo
- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange {
NSParameterAssert(label != nil);
CGSize labelSize = label.bounds.size;
// create instances of NSLayoutManager, NSTextContainer and NSTextStorage
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:labelSize];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];
// configure layoutManager and textStorage
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
// configure textContainer for the label
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;
// find the tapped character location and compare it to the specified range
CGPoint locationOfTouchInLabel = [self locationInView:label];
CGRect textBoundingBox = [layoutManager usedRectForTextContainer:textContainer];
CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
locationOfTouchInLabel.y - textContainerOffset.y);
NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer
inTextContainer:textContainer
fractionOfDistanceBetweenInsertionPoints:nil];
if (NSLocationInRange(indexOfCharacter, targetRange)) {
return YES;
} else {
return NO;
}
}
也可以根据自己的业务需求,在代码里做一些改动,代码不多,简单易懂,不改动的可以直接拿我的分类用。