iOS微信朋友圈的點贊、評論功能,即點擊昵稱跳轉到個人主頁
很多社交軟件,都會有這么一個功能,就是點擊部分文字,觸發一個事件,像微信朋友圈、QQ空間那樣點擊評論者的昵稱跳轉到個人主頁,或者點擊點贊人的昵稱跳轉到個人主頁。
本菜鳥在做社交項目的時候也遇到這個問題,一開始不知道怎么處理好。找了網上的方法,都是說:1:昵稱上面覆蓋一個UIButton 。 2:設置文字部分區域,觸發事件。
本菜鳥覺得第二種方法可靠,于是找了第三方,比如:TTTAttributedLabel、YYText 、RTLabel都是很不錯的第三方富文本。但是本菜鳥又突發奇想,我只是要實現這個簡單的功能,強大的蘋果應該不至于這么傻吧,于是就想著這么折騰一個不用第三方的辦法出來,然后就.... 你懂的 ... 下面直接貼效果圖 ... 不喜勿噴,大神多多指教。</code></pre>
評論的效果圖:

點贊的效果圖:

直接貼代碼:(調用UILable的擴展類)
在使用的地方調用方法:
-(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>
下面是UILable擴展類
#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>
#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>
來自:http://www.jianshu.com/p/34369aed6789