iOS微信朋友圈的点赞、评论功能,即点击昵称跳转到个人主页
pskz8116
9年前
<pre> <code class="language-objectivec">很多社交软件,都会有这么一个功能,就是点击部分文字,触发一个事件,像微信朋友圈、QQ空间那样点击评论者的昵称跳转到个人主页,或者点击点赞人的昵称跳转到个人主页。 本菜鸟在做社交项目的时候也遇到这个问题,一开始不知道怎么处理好。找了网上的方法,都是说:1:昵称上面覆盖一个UIButton 。 2:设置文字部分区域,触发事件。 本菜鸟觉得第二种方法可靠,于是找了第三方,比如:TTTAttributedLabel、YYText 、RTLabel都是很不错的第三方富文本。但是本菜鸟又突发奇想,我只是要实现这个简单的功能,强大的苹果应该不至于这么傻吧,于是就想着这么折腾一个不用第三方的办法出来,然后就.... 你懂的 ... 下面直接贴效果图 ... 不喜勿喷,大神多多指教。</code></pre> <p>评论的效果图:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/f3618372f4f5346eca6792a4b2ea4821.png"></p> <p>点赞的效果图:</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/994eb74cb80c2eb3485708b20f9d9477.png"></p> <p>直接贴代码:(调用UILable的扩展类)</p> <p>在使用的地方调用方法:</p> <pre> <code class="language-objectivec">-(void)richTextLable { //评论 NSString *nameOne = @"张三"; NSString *nameTwo = @"我是李四"; NSString *replyString = @"您好,您现在在干什么,么么哒。这个功能应该满足需求了。"; NSString *totalString = [NSString stringWithFormat:@"%@回复%@:%@",nameOne,nameTwo,replyString]; NSMutableAttributedString *newString = [[NSMutableAttributedString alloc] initWithString:totalString]; [newString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, totalString.length)]; [newString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, nameOne.length)]; [newString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(nameOne.length+2, nameTwo.length)]; //设置行距 实际开发中间距为0太丑了,根据项目需求自己把握 NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; style.lineSpacing = 3; [newString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, totalString.length)]; UILabel *richTextLbl = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, self.view.bounds.size.width - 40, 20)]; //richTextLbl.backgroundColor = [UIColor greenColor]; richTextLbl.numberOfLines = 0;//设置UILable自适应 richTextLbl.attributedText = newString; [self.view addSubview:richTextLbl]; [richTextLbl sizeToFit]; [richTextLbl onTapRangeActionWithString:@[nameOne,nameTwo] tapClicked:^(NSString *string, NSRange range, NSInteger index) { NSLog(@"点击了-->--%@ ---下标:%ld",string,index); }]; //点赞 UILabel *goodLbl = [[UILabel alloc] init]; goodLbl.frame = CGRectMake(20, 170, 100, 20); goodLbl.text = @"点赞的:"; [self.view addSubview:goodLbl]; NSArray *goodArray = @[@"张三",@"李四",@"王五",@"李兆",@"粟子",@"小李",@"李四",@"王五",@"李兆",@"粟子",@"小李"]; NSString *goodTotalString = [goodArray componentsJoinedByString:@", "]; NSMutableAttributedString *newGoodString = [[NSMutableAttributedString alloc] initWithString:goodTotalString]; [newGoodString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16] range:NSMakeRange(0, goodTotalString.length)]; //设置行距 实际开发中间距为0太丑了,根据项目需求自己把握 NSMutableParagraphStyle *paragraphstyle = [[NSMutableParagraphStyle alloc] init]; paragraphstyle.lineSpacing = 3; [newGoodString addAttribute:NSParagraphStyleAttributeName value:paragraphstyle range:NSMakeRange(0, goodTotalString.length)]; UILabel *goodTextLbl = [[UILabel alloc] initWithFrame:CGRectMake(20, 200, self.view.bounds.size.width - 40, 20)]; goodTextLbl.backgroundColor = [UIColor orangeColor]; goodTextLbl.numberOfLines = 0;//设置UILable自适应 goodTextLbl.attributedText = newGoodString; [self.view addSubview:goodTextLbl]; [goodTextLbl sizeToFit]; [goodTextLbl onTapRangeActionWithString:goodArray tapClicked:^(NSString *string, NSRange range, NSInteger index) { NSLog(@"这是第--%ld--个点赞的,他是--%@",index,string); }]; }</code></pre> <p>下面是UILable扩展类</p> <pre> <code class="language-objectivec">#import <UIKit/UIKit.h> @interface XLRichTextModel : NSObject @property (nonatomic, copy) NSString *string; @property (nonatomic, assign) NSRange range; @end @interface UILabel (Category) ///是否显示点击效果,默认是打开 @property (nonatomic, assign) BOOL isShowTagEffect; ///TagArray 点击的字符串数组 - (void)onTapRangeActionWithString:(NSArray <NSString *> *)TagArray tapClicked:(void (^) (NSString *string , NSRange range , NSInteger index))tapClick; @end</code></pre> <pre> <code class="language-objectivec">#import "UILabel+Category.h" #import <objc/runtime.h> #import <CoreText/CoreText.h> #import <Foundation/Foundation.h> @implementation XLRichTextModel @end @implementation UILabel (Category) #pragma mark - AssociatedObjects - (NSMutableArray *)attributeStrings { return objc_getAssociatedObject(self, _cmd); } - (void)setAttributeStrings:(NSMutableArray *)attributeStrings { objc_setAssociatedObject(self, @selector(attributeStrings), attributeStrings, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSMutableDictionary *)effectDic { return objc_getAssociatedObject(self, _cmd); } - (void)setEffectDic:(NSMutableDictionary *)effectDic { objc_setAssociatedObject(self, @selector(effectDic), effectDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isTapAction { return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (void)setIsTapAction:(BOOL)isTapAction { objc_setAssociatedObject(self, @selector(isTapAction), @(isTapAction), OBJC_ASSOCIATION_ASSIGN); } - (void (^)(NSString *, NSRange, NSInteger))tapBlock { return objc_getAssociatedObject(self, _cmd); } - (void)setTapBlock:(void (^)(NSString *, NSRange, NSInteger))tapBlock { objc_setAssociatedObject(self, @selector(tapBlock), tapBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (BOOL)isShowTagEffect { return [objc_getAssociatedObject(self, _cmd) boolValue]; } -(void)setIsShowTagEffect:(BOOL)isShowTagEffect { objc_setAssociatedObject(self, @selector(isShowTagEffect), @(isShowTagEffect), OBJC_ASSOCIATION_ASSIGN); self.isTapEffect = isShowTagEffect; } - (BOOL)isTapEffect { return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (void)setIsTapEffect:(BOOL)isTapEffect { objc_setAssociatedObject(self, @selector(isTapEffect), @(isTapEffect), OBJC_ASSOCIATION_ASSIGN); } #pragma mark - 事件方法 - (void)onTapRangeActionWithString:(NSArray <NSString *> *)TagArray tapClicked:(void (^) (NSString *string , NSRange range , NSInteger index))tapClick { //获取部分点击的区域 [self tagRangeActionWithString:TagArray]; if (self.tapBlock != tapClick) { self.tapBlock = tapClick; } } #pragma mark - touchAction - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (!self.isTapAction) { return; } if (objc_getAssociatedObject(self, @selector(isShowTagEffect))) { self.isTapEffect = self.isShowTagEffect; } UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; __weak typeof(self) weakSelf = self; [self tapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) { if (weakSelf.tapBlock) { weakSelf.tapBlock (string , range , index); } if (self.isTapEffect) { [self saveEffectDicWithRange:range]; [self tapEffectWithStatus:YES]; } }]; } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (self.isTapAction) { if ([self tapFrameWithTouchPoint:point result:nil]) { return self; } } return [super hitTest:point withEvent:event]; } #pragma mark - 获取点击的范围 - (BOOL)tapFrameWithTouchPoint:(CGPoint)point result:(void (^) (NSString *string , NSRange range , NSInteger index))resultBlock { CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attributedText); CGMutablePathRef Path = CGPathCreateMutable(); CGPathAddRect(Path, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)); CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL); CFRange range = CTFrameGetVisibleStringRange(frame); if (self.attributedText.length > range.length) { UIFont *font ; if ([self.attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:nil]) { font = [self.attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:nil]; }else if (self.font){ font = self.font; }else { font = [UIFont systemFontOfSize:17]; } Path = CGPathCreateMutable(); CGPathAddRect(Path, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height + font.lineHeight)); frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL); } CFArrayRef lines = CTFrameGetLines(frame); if (!lines) { return NO; } CFIndex count = CFArrayGetCount(lines); CGPoint origins[count]; CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); CGAffineTransform transform = [self transformForCoreText]; CGFloat verticalOffset = 0; for (CFIndex i = 0; i < count; i++) { CGPoint linePoint = origins[i]; CTLineRef line = CFArrayGetValueAtIndex(lines, i); CGRect flippedRect = [self lineBounds:line point:linePoint]; CGRect rect = CGRectApplyAffineTransform(flippedRect, transform); rect = CGRectInset(rect, 0, 0); rect = CGRectOffset(rect, 0, verticalOffset); NSParagraphStyle *style = [self.attributedText attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:nil]; CGFloat lineSpace; if (style) { lineSpace = style.lineSpacing; }else { lineSpace = 0; } CGFloat lineOutSpace = (self.bounds.size.height - lineSpace * (count - 1) -rect.size.height * count) / 2; rect.origin.y = lineOutSpace + rect.size.height * i + lineSpace * i; if (CGRectContainsPoint(rect, point)) { CGPoint relativePoint = CGPointMake(point.x - CGRectGetMinX(rect), point.y - CGRectGetMinY(rect)); CFIndex index = CTLineGetStringIndexForPosition(line, relativePoint); CGFloat offset; CTLineGetOffsetForStringIndex(line, index, &offset); if (offset > relativePoint.x) { index = index - 1; } NSInteger link_count = self.attributeStrings.count; for (int j = 0; j < link_count; j++) { XLRichTextModel *model = self.attributeStrings[j]; NSRange link_range = model.range; if (NSLocationInRange(index, link_range)) { if (resultBlock) { resultBlock (model.string , model.range , (NSInteger)j); } return YES; } } } } return NO; } - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.isTapEffect) { [self performSelectorOnMainThread:@selector(tapEffectWithStatus:) withObject:nil waitUntilDone:NO]; } } - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.isTapEffect) { [self performSelectorOnMainThread:@selector(tapEffectWithStatus:) withObject:nil waitUntilDone:NO]; } } - (CGAffineTransform)transformForCoreText { return CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f); } - (CGRect)lineBounds:(CTLineRef)line point:(CGPoint)point { CGFloat ascent = 0.0f; CGFloat descent = 0.0f; CGFloat leading = 0.0f; CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading); CGFloat height = ascent + fabs(descent) + leading; return CGRectMake(point.x, point.y , width, height); } #pragma mark - 是否显示点击效果 默认是 项目中一般是现实点击效果 - (void)tapEffectWithStatus:(BOOL)status { if (self.isTapEffect) { NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; NSMutableAttributedString *subAtt = [[NSMutableAttributedString alloc] initWithAttributedString:[[self.effectDic allValues] firstObject]]; NSRange range = NSRangeFromString([[self.effectDic allKeys] firstObject]); if (status) { [subAtt addAttribute:NSBackgroundColorAttributeName value:[UIColor lightGrayColor] range:NSMakeRange(0, subAtt.string.length)]; [attStr replaceCharactersInRange:range withAttributedString:subAtt]; }else { [attStr replaceCharactersInRange:range withAttributedString:subAtt]; } self.attributedText = attStr; } } - (void)saveEffectDicWithRange:(NSRange)range { self.effectDic = [NSMutableDictionary dictionary]; NSAttributedString *subAttribute = [self.attributedText attributedSubstringFromRange:range]; [self.effectDic setObject:subAttribute forKey:NSStringFromRange(range)]; } #pragma mark - 获取部分点击的区域 - (void)tagRangeActionWithString:(NSArray <NSString *> *)strings { if (self.attributedText == nil) { self.isTapAction = NO; return; } self.isTapAction = YES; self.isTapEffect = YES; __block NSString *totalStr = self.attributedText.string; self.attributeStrings = [NSMutableArray array]; __weak typeof(self) weakSelf = self; [strings enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSRange range = [totalStr rangeOfString:obj]; if (range.length != 0) { totalStr = [totalStr stringByReplacingCharactersInRange:range withString:[weakSelf stringWithRange:range]]; XLRichTextModel *model = [XLRichTextModel new]; model.range = range; model.string = obj; [weakSelf.attributeStrings addObject:model]; } }]; } - (NSString *)stringWithRange:(NSRange)range { NSMutableString *string = [NSMutableString string]; for (int i = 0; i < range.length ; i++) { [string appendString:@" "]; } return string; } @end</code></pre> <p> </p> <p>来自:http://www.jianshu.com/p/34369aed6789</p> <p> </p>