Created
April 17, 2019 10:43
-
-
Save HamGuy/8ed1312996601392e50c7e384c70a893 to your computer and use it in GitHub Desktop.
HGLabel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HGLabel.h | |
// | |
// Created by HamGuy on 2019/4/17. | |
// | |
#import <UIKit/UIKit.h> | |
NS_ASSUME_NONNULL_BEGIN | |
@class HGLabel; | |
@protocol HGLabelDelegate <NSObject> | |
-(void)label:(HGLabel *)label didTapURL:(NSString *)url atRange:(NSRange)range; | |
@end | |
/** | |
可实现链接高亮显示,点击跳转以及长按复制功能的 Label | |
*/ | |
@interface HGLabel : UILabel | |
/** | |
链接颜色 | |
*/ | |
@property (nonatomic, strong) IBInspectable UIColor* linkColor; | |
/** | |
是否可长按复制 | |
*/ | |
@property (nonatomic, assign) IBInspectable BOOL longPressAble; | |
/** | |
长安时间,默认 1 秒 | |
*/ | |
@property (nonatomic, assign) IBInspectable NSTimeInterval timeToPress; | |
//@property (nonatomic, readonly ,strong) NSLayoutManager* layoutManager; | |
//@property (nonatomic, readonly ,strong) NSTextContainer* textContainer; | |
//@property (nonatomic, readonly ,strong) NSTextStorage* textStorage; | |
@property (nonatomic, weak) id<CCLabelDelegate> delegate; | |
/** | |
添加链接 | |
@param linkText 链接文字 | |
@param urlString 跳转 URL | |
*/ | |
-(void)addLink:(NSString *)linkText withURL:(NSString *)urlString; | |
@end | |
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HGLabell.m | |
// | |
// Created by HamGuy on 2019/4/17. | |
// | |
#import "HGLabel.h" | |
@interface HGLabel () | |
@property (nonatomic, strong) NSLayoutManager* layoutManager; | |
@property (nonatomic, strong) NSTextContainer* textContainer; | |
@property (nonatomic, strong) NSTextStorage* textStorage; | |
@property (nonatomic, strong) NSMutableDictionary* linkInfo; | |
@end | |
@implementation HGLabel | |
@synthesize attributedText = _attributedText; | |
@synthesize numberOfLines = _numberOfLines; | |
@synthesize lineBreakMode = _lineBreakMode; | |
- (instancetype)initWithFrame:(CGRect)frame | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) { | |
[self setup]; | |
} | |
return self; | |
} | |
- (instancetype)initWithCoder:(NSCoder *)coder | |
{ | |
self = [super initWithCoder:coder]; | |
if (self) { | |
[self setup]; | |
} | |
return self; | |
} | |
#pragma mark Override | |
- (void)drawTextInRect:(CGRect)rect { | |
// [super drawTextInRect:rect]; | |
NSRange range = [self.layoutManager glyphRangeForTextContainer:self.textContainer]; | |
// CGRect tmpRect = [self.layoutManager boundingRectForGlyphRange:range inTextContainer:self.textContainer]; | |
// CGFloat offsetY = (self.bounds.size.height - tmpRect.size.height) ; | |
// CGPoint offset = CGPointMake(0, offsetY); | |
[self.layoutManager drawBackgroundForGlyphRange:range atPoint:CGPointZero]; | |
[self.layoutManager drawGlyphsForGlyphRange:range atPoint:CGPointZero]; | |
} | |
-(void)setTextStorage:(NSTextStorage *)textStorage { | |
_textStorage = textStorage; | |
[self.textStorage addLayoutManager:self.layoutManager]; | |
} | |
-(NSAttributedString *)attributedText { | |
return _attributedText; | |
} | |
- (void)setAttributedText:(NSAttributedString *)attributedText { | |
_attributedText = attributedText; | |
if(!attributedText) { | |
// self.textStorage = [[NSTextStorage alloc] init]; | |
self.linkInfo = [@{} mutableCopy]; | |
} else { | |
self.textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText]; | |
[self.textStorage setAttributedString:attributedText]; | |
} | |
[self setNeedsDisplay]; | |
} | |
-(NSInteger)numberOfLines { | |
return _numberOfLines; | |
} | |
- (void)setNumberOfLines:(NSInteger)numberOfLines { | |
_numberOfLines = numberOfLines; | |
self.textContainer.maximumNumberOfLines = numberOfLines; | |
} | |
- (NSLineBreakMode)lineBreakMode { | |
return _lineBreakMode; | |
} | |
- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode { | |
_lineBreakMode = lineBreakMode; | |
self.textContainer.lineBreakMode = lineBreakMode; | |
} | |
- (BOOL)canBecomeFirstResponder { | |
return YES; | |
} | |
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { | |
return self.longPressAble && action == @selector(copyText); | |
} | |
- (void)layoutSubviews { | |
[super layoutSubviews]; | |
self.textContainer.size = self.bounds.size; | |
} | |
//- (CGRect)boundingRectWithSize:(CGSize)size | |
//{ | |
// self.textContainer.size = size; | |
// | |
// NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer]; | |
// | |
// CGRect boundingRect = [self.layoutManager boundingRectForGlyphRange:glyphRange | |
// inTextContainer:self.textContainer]; | |
// boundingRect.origin.x = 0; | |
// boundingRect.origin.y = 0; | |
// | |
// return boundingRect; | |
//} | |
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | |
UITouch *touch = touches.anyObject; | |
CGPoint locationOfTouch = [touch locationInView:self]; | |
_textContainer.size = self.bounds.size; | |
NSUInteger indexOfCharacter = [_layoutManager glyphIndexForPoint:locationOfTouch inTextContainer:_textContainer]; | |
__weak typeof(self) weakSelf = self; | |
[_linkInfo enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSValue* obj, BOOL * _Nonnull stop) { | |
NSRange range = obj.rangeValue; | |
if (NSLocationInRange(indexOfCharacter, range) && [weakSelf.delegate respondsToSelector:@selector(label:didTapURL:atRange:)]) { | |
[weakSelf.delegate label:weakSelf didTapURL:key atRange:range]; | |
} | |
}]; | |
} | |
#pragma mark Public | |
- (void)addLink:(NSString *)linkText withURL:(NSString *)urlString { | |
NSString *text = self.attributedText.string; | |
if(!text) { | |
return; | |
} | |
NSRange range = [text rangeOfString:linkText]; | |
if(range.location != NSNotFound) { | |
self.linkInfo[urlString] = [NSValue valueWithRange:range]; | |
} | |
} | |
#pragma mark Private | |
-(void)setup { | |
self.timeToPress = 1; | |
self.userInteractionEnabled = YES; | |
UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)]; | |
gesture.minimumPressDuration = _timeToPress; | |
[self addGestureRecognizer:gesture]; | |
self.linkInfo = [@{} mutableCopy]; | |
self.layoutManager = [NSLayoutManager new]; | |
self.textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero]; | |
[self.layoutManager addTextContainer:self.textContainer]; | |
self.textContainer.lineFragmentPadding = 0; | |
self.textContainer.maximumNumberOfLines = 0; | |
self.textContainer.lineBreakMode = self.lineBreakMode; | |
} | |
- (void)longPress:(UIGestureRecognizer *)gesture { | |
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:@"复制" action:@selector(copyText)]; | |
UIMenuController *menuController = [UIMenuController sharedMenuController]; | |
menuController.menuItems = @[item]; | |
[menuController setTargetRect:self.frame inView:self]; | |
[menuController setMenuVisible:YES animated:YES]; | |
} | |
-(void)copyText { | |
NSString *text = self.attributedText.string; | |
if(!text) { | |
self.text = text; | |
} | |
[UIPasteboard generalPasteboard].string = text; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment