Ios 创建tap able";链接“;在UILabel的NSAttribute字符串中?

Ios 创建tap able";链接“;在UILabel的NSAttribute字符串中?,ios,hyperlink,uilabel,nsattributedstring,uitapgesturerecognizer,Ios,Hyperlink,Uilabel,Nsattributedstring,Uitapgesturerecognizer,我已经搜索了好几个小时了,但是我失败了。我甚至不知道我应该找什么 许多应用程序都有文本,本文中的文本是圆角矩形中的web超链接。当我单击它们时,UIWebView打开。让我困惑的是,它们通常有自定义链接,例如,如果单词以#开头,它也可以单击,应用程序通过打开另一个视图来响应。我该怎么做?是否可以使用UILabel或我需要UITextView或其他东西?UITextView支持OS3.0中的数据检测器,而UILabel不支持 如果您在UITextView上启用数据检测器,并且您的文本包含URL、电

我已经搜索了好几个小时了,但是我失败了。我甚至不知道我应该找什么


许多应用程序都有文本,本文中的文本是圆角矩形中的web超链接。当我单击它们时,
UIWebView
打开。让我困惑的是,它们通常有自定义链接,例如,如果单词以#开头,它也可以单击,应用程序通过打开另一个视图来响应。我该怎么做?是否可以使用
UILabel
或我需要
UITextView
或其他东西?

UITextView
支持OS3.0中的数据检测器,而
UILabel
不支持


如果您在
UITextView
上启用数据检测器,并且您的文本包含URL、电话号码等。它们将显示为链接。

对于完全自定义的链接,您需要使用UIWebView-您可以拦截呼叫,因此,当按下链接时,您可以转到应用程序的其他部分。

如果您没有设置任何图像,UIButtonTypeCustom是一个可单击的标签

以下是超链接UILabel的示例代码: 资料来源:


通常,如果我们希望UILabel显示文本中的可点击链接,我们需要解决两个独立的任务:

  • 将部分文本的外观更改为链接
  • 检测和处理链接上的接触(打开URL是一种特殊情况)
  • 第一个很简单。从iOS 6开始,UILabel支持显示属性字符串。您只需创建和配置NSMutableAttributeString的实例:

    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"String with a link" attributes:nil];
    NSRange linkRange = NSMakeRange(14, 4); // for the word "link" in the string above
    
    NSDictionary *linkAttributes = @{ NSForegroundColorAttributeName : [UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0],
                                      NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) };
    [attributedString setAttributes:linkAttributes range:linkRange];
    
    // Assign attributedText to UILabel
    label.attributedText = attributedString;
    
    就这样!上面的代码使UILabel显示带有

    现在,我们应该检测此链接上的接触。其想法是捕获UILabel中的所有点击,并确定点击位置是否足够靠近链接。为了捕捉触摸,我们可以在标签上添加轻触手势识别器。确保为标签启用userInteraction,默认情况下它处于关闭状态:

    label.userInteractionEnabled = YES;
    [label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]]; 
    
    现在最复杂的事情是:找出点击是否在链接显示的位置,而不是标签的任何其他部分。如果我们使用单线UILabel,那么通过对链接显示的区域边界进行硬编码,可以相对轻松地解决此任务,但是让我们更优雅地解决此问题,对于一般情况,多线UILabel不需要对链接布局有初步了解

    其中一种方法是使用iOS 7中引入的文本工具包API的功能:

    // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
    
    // Configure layoutManager and textStorage
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];
    
    // Configure textContainer
    textContainer.lineFragmentPadding = 0.0;
    textContainer.lineBreakMode = label.lineBreakMode;
    textContainer.maximumNumberOfLines = label.numberOfLines;
    
    将创建和配置的NSLayoutManager、NSTextContainer和NSTextStorage实例保存在类(很可能是UIViewController的后代)的属性中—我们将在其他方法中需要它们

    现在,每次标签更改其边框时,都要更新textContainer的大小:

    - (void)viewDidLayoutSubviews
    {
        [super viewDidLayoutSubviews];
        self.textContainer.size = self.label.bounds.size;
    }
    
    最后,检测水龙头是否正好位于链接上:

    - (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture
    {
        CGPoint locationOfTouchInLabel = [tapGesture locationInView:tapGesture.view];
        CGSize labelSize = tapGesture.view.bounds.size;
        CGRect textBoundingBox = [self.layoutManager usedRectForTextContainer:self.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 = [self.layoutManager characterIndexForPoint:locationOfTouchInTextContainer
                                                                inTextContainer:self.textContainer
                                       fractionOfDistanceBetweenInsertionPoints:nil];
        NSRange linkRange = NSMakeRange(14, 4); // it's better to save the range somewhere when it was originally used for marking link in attributed string
        if (NSLocationInRange(indexOfCharacter, linkRange)) {
            // Open an URL, or handle the tap on the link in any other way
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];
        }
    }
    
    (我的答案建立在@NAlexN的基础上。我不会在这里重复他对每个步骤的详细解释。)

    我发现将可点击的UILabel文本作为类别添加到UITapGestureRecognizer最方便、最直接。(正如一些答案所建议的,您不必使用UITextView的数据检测器。)

    将以下方法添加到UITapgestureRecognitor类别:

    /**
     Returns YES if the tap gesture was within the specified range of the attributed text of the label.
     */
    - (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:CGSizeZero];
        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;
        textContainer.size = labelSize;
    
        // 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;
        }
    }
    
    示例代码 手势回叫
    我创建了名为的UILabel子类,它基于IOS7中引入的textkit API。它使用的方法与作者建议的方法相同。它提供了在文本中指定要搜索的模式的灵活性。您可以指定要应用于这些模式的样式,以及在点击模式时要执行的操作

    //Detects email in text
    
     NSString *emailRegexString = @"[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}";
     NSError *error;
     NSRegularExpression *regex = [[NSRegularExpression alloc]initWithPattern:emailRegexString options:0 error:&error];
     PatternDescriptor *descriptor = [[PatternDescriptor alloc]initWithRegex:regex withSearchType:PatternSearchTypeAll withPatternAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
     [self.customLabel enablePatternDetection:descriptor];
    
    如果你想让一个字符串可以点击,你可以这样做。此代码将属性应用于字符串“text”的每个匹配项


    我很难处理这件事。。。UILabel上带有属性文本上的链接。。。这只是一个令人头痛的问题,所以我最终使用了。

    使用以下.h和.m文件创建类。在.m文件中有以下函数

     - (void)linkAtPoint:(CGPoint)location
    
    在这个函数中,我们将检查需要执行操作的子字符串的范围。使用您自己的逻辑来设置范围

    下面是子类的用法

    TaggedLabel *label = [[TaggedLabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [self.view addSubview:label];
    label.numberOfLines = 0;
    NSMutableAttributedString *attributtedString = [[NSMutableAttributedString alloc] initWithString : @"My name is @jjpp" attributes : @{ NSFontAttributeName : [UIFont systemFontOfSize:10],}];                                                                                                                                                                              
    //Do not forget to add the font attribute.. else it wont work.. it is very important
    [attributtedString addAttribute:NSForegroundColorAttributeName
                            value:[UIColor redColor]
                            range:NSMakeRange(11, 5)];//you can give this range inside the .m function mentioned above
    
    下面是.h文件

    #import <UIKit/UIKit.h>
    
    @interface TaggedLabel : UILabel<NSLayoutManagerDelegate>
    
    @property(nonatomic, strong)NSLayoutManager *layoutManager;
    @property(nonatomic, strong)NSTextContainer *textContainer;
    @property(nonatomic, strong)NSTextStorage *textStorage;
    @property(nonatomic, strong)NSArray *tagsArray;
    @property(readwrite, copy) tagTapped nameTagTapped;
    
    @end   
    
    #导入
    @接口标签标签:UILabel
    @属性(非原子,强)NSLayoutManager*layoutManager;
    @属性(非原子,强)NSTextContainer*textContainer;
    @属性(非原子,强)NSTextStorage*textStorage;
    @属性(非原子,强)NSArray*tagsArray;
    @属性(读写、复制)tagTapped名称tagTapped;
    @结束
    
    下面是.m文件

    #import "TaggedLabel.h"
    @implementation TaggedLabel
    
    - (id)initWithFrame:(CGRect)frame
    {
     self = [super initWithFrame:frame];
     if (self)
     {
      self.userInteractionEnabled = YES;
     }
    return self;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
     self = [super initWithCoder:aDecoder];
    if (self)
    {
     self.userInteractionEnabled = YES;
    }
    return self;
    }
    
    - (void)setupTextSystem
    {
     _layoutManager = [[NSLayoutManager alloc] init];
     _textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
     _textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText];
     // Configure layoutManager and textStorage
     [_layoutManager addTextContainer:_textContainer];
     [_textStorage addLayoutManager:_layoutManager];
     // Configure textContainer
     _textContainer.lineFragmentPadding = 0.0;
     _textContainer.lineBreakMode = NSLineBreakByWordWrapping;
     _textContainer.maximumNumberOfLines = 0;
     self.userInteractionEnabled = YES;
     self.textContainer.size = self.bounds.size;
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
     if (!_layoutManager)
     {
      [self setupTextSystem];
     }
     // Get the info for the touched link if there is one
     CGPoint touchLocation = [[touches anyObject] locationInView:self];
     [self linkAtPoint:touchLocation];
    }
    
    - (void)linkAtPoint:(CGPoint)location
    {
     // Do nothing if we have no text
     if (_textStorage.string.length == 0)
     {
      return;
     }
     // Work out the offset of the text in the view
     CGPoint textOffset = [self calcGlyphsPositionInView];
     // Get the touch location and use text offset to convert to text cotainer coords
     location.x -= textOffset.x;
     location.y -= textOffset.y;
     NSUInteger touchedChar = [_layoutManager glyphIndexForPoint:location inTextContainer:_textContainer];
     // If the touch is in white space after the last glyph on the line we don't
     // count it as a hit on the text
     NSRange lineRange;
     CGRect lineRect = [_layoutManager lineFragmentUsedRectForGlyphAtIndex:touchedChar effectiveRange:&lineRange];
     if (CGRectContainsPoint(lineRect, location) == NO)
     {
      return;
     }
     // Find the word that was touched and call the detection block
        NSRange range = NSMakeRange(11, 5);//for this example i'm hardcoding the range here. In a real scenario it should be iterated through an array for checking all the ranges
        if ((touchedChar >= range.location) && touchedChar < (range.location + range.length))
        {
         NSLog(@"range-->>%@",self.tagsArray[i][@"range"]);
        }
    }
    
    - (CGPoint)calcGlyphsPositionInView
    {
     CGPoint textOffset = CGPointZero;
     CGRect textBounds = [_layoutManager usedRectForTextContainer:_textContainer];
     textBounds.size.width = ceil(textBounds.size.width);
     textBounds.size.height = ceil(textBounds.size.height);
    
     if (textBounds.size.height < self.bounds.size.height)
     {
      CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0;
      textOffset.y = paddingHeight;
     }
    
     if (textBounds.size.width < self.bounds.size.width)
     {
      CGFloat paddingHeight = (self.bounds.size.width - textBounds.size.width) / 2.0;
      textOffset.x = paddingHeight;
     }
     return textOffset;
     }
    
    @end
    
    #导入“TaggedLabel.h”
    @实现标记标签
    -(id)initWithFrame:(CGRect)帧
    {
    self=[super initWithFrame:frame];
    如果(自我)
    {
    self.userInteractionEnabled=是;
    }
    回归自我;
    }
    -(id)initWithCoder:(NSCoder*)aDecoder
    {
    self=[super initWithCoder:aDecoder];
    如果(自我)
    {
    self.userInteractionEnabled=是;
    }
    回归自我;
    }
    -(无效)设置文本系统
    {
    _layoutManager=[[NSLayoutManager alloc]init];
    _textContainer=[[NSTextContainer alloc]initWithSize:CGSizeZero];
    _textStorage=[[NSTextStorage alloc]initWithAttributedString:self.attributedText];
    //配置layoutManager和textStorage
    [\u layoutManager addTextContainer:\u textContainer];
    [\u textStorage addLayoutManager:\u layoutManager];
    //配置文本容器
    _textContainer.lineFragmentPadding=0.0;
    _textContainer.lineBreakMode=NSLineBreakByWordWrapping;
    _textContainer.maximumNumberOfLines=0;
    self.userInteractionEnabled=是;
    self.textContainer.size=self.bounds.size;
    }
    -(无效)触摸开始:(NSSet*)触摸事件:(UIEvent*)事件
    {
    如果(!\u布局经理)
    {
    [自设置文本系统];
    }
    //如果有,请获取触摸链接的信息
    CGPoint touchLocation=[[Touchs anyObject]locationInView:self];
    [自链接点:触摸位置];
    }
    -(无效)链接点:(CGPoint)位置
    {
    //如果我们没有文本,什么也不做
    如果(_
    
     - (void)linkAtPoint:(CGPoint)location
    
    TaggedLabel *label = [[TaggedLabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [self.view addSubview:label];
    label.numberOfLines = 0;
    NSMutableAttributedString *attributtedString = [[NSMutableAttributedString alloc] initWithString : @"My name is @jjpp" attributes : @{ NSFontAttributeName : [UIFont systemFontOfSize:10],}];                                                                                                                                                                              
    //Do not forget to add the font attribute.. else it wont work.. it is very important
    [attributtedString addAttribute:NSForegroundColorAttributeName
                            value:[UIColor redColor]
                            range:NSMakeRange(11, 5)];//you can give this range inside the .m function mentioned above
    
    #import <UIKit/UIKit.h>
    
    @interface TaggedLabel : UILabel<NSLayoutManagerDelegate>
    
    @property(nonatomic, strong)NSLayoutManager *layoutManager;
    @property(nonatomic, strong)NSTextContainer *textContainer;
    @property(nonatomic, strong)NSTextStorage *textStorage;
    @property(nonatomic, strong)NSArray *tagsArray;
    @property(readwrite, copy) tagTapped nameTagTapped;
    
    @end   
    
    #import "TaggedLabel.h"
    @implementation TaggedLabel
    
    - (id)initWithFrame:(CGRect)frame
    {
     self = [super initWithFrame:frame];
     if (self)
     {
      self.userInteractionEnabled = YES;
     }
    return self;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
     self = [super initWithCoder:aDecoder];
    if (self)
    {
     self.userInteractionEnabled = YES;
    }
    return self;
    }
    
    - (void)setupTextSystem
    {
     _layoutManager = [[NSLayoutManager alloc] init];
     _textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
     _textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText];
     // Configure layoutManager and textStorage
     [_layoutManager addTextContainer:_textContainer];
     [_textStorage addLayoutManager:_layoutManager];
     // Configure textContainer
     _textContainer.lineFragmentPadding = 0.0;
     _textContainer.lineBreakMode = NSLineBreakByWordWrapping;
     _textContainer.maximumNumberOfLines = 0;
     self.userInteractionEnabled = YES;
     self.textContainer.size = self.bounds.size;
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
     if (!_layoutManager)
     {
      [self setupTextSystem];
     }
     // Get the info for the touched link if there is one
     CGPoint touchLocation = [[touches anyObject] locationInView:self];
     [self linkAtPoint:touchLocation];
    }
    
    - (void)linkAtPoint:(CGPoint)location
    {
     // Do nothing if we have no text
     if (_textStorage.string.length == 0)
     {
      return;
     }
     // Work out the offset of the text in the view
     CGPoint textOffset = [self calcGlyphsPositionInView];
     // Get the touch location and use text offset to convert to text cotainer coords
     location.x -= textOffset.x;
     location.y -= textOffset.y;
     NSUInteger touchedChar = [_layoutManager glyphIndexForPoint:location inTextContainer:_textContainer];
     // If the touch is in white space after the last glyph on the line we don't
     // count it as a hit on the text
     NSRange lineRange;
     CGRect lineRect = [_layoutManager lineFragmentUsedRectForGlyphAtIndex:touchedChar effectiveRange:&lineRange];
     if (CGRectContainsPoint(lineRect, location) == NO)
     {
      return;
     }
     // Find the word that was touched and call the detection block
        NSRange range = NSMakeRange(11, 5);//for this example i'm hardcoding the range here. In a real scenario it should be iterated through an array for checking all the ranges
        if ((touchedChar >= range.location) && touchedChar < (range.location + range.length))
        {
         NSLog(@"range-->>%@",self.tagsArray[i][@"range"]);
        }
    }
    
    - (CGPoint)calcGlyphsPositionInView
    {
     CGPoint textOffset = CGPointZero;
     CGRect textBounds = [_layoutManager usedRectForTextContainer:_textContainer];
     textBounds.size.width = ceil(textBounds.size.width);
     textBounds.size.height = ceil(textBounds.size.height);
    
     if (textBounds.size.height < self.bounds.size.height)
     {
      CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0;
      textOffset.y = paddingHeight;
     }
    
     if (textBounds.size.width < self.bounds.size.width)
     {
      CGFloat paddingHeight = (self.bounds.size.width - textBounds.size.width) / 2.0;
      textOffset.x = paddingHeight;
     }
     return textOffset;
     }
    
    @end
    
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"This is an example by @marcelofabri_"];
    [attributedString addAttribute:NSLinkAttributeName
                         value:@"username://marcelofabri_"
                         range:[[attributedString string] rangeOfString:@"@marcelofabri_"]];
    
    NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor],
                                 NSUnderlineColorAttributeName: [UIColor lightGrayColor],
                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};
    
    // assume that textView is a UITextView previously created (either by code or Interface Builder)
    textView.linkTextAttributes = linkAttributes; // customizes the appearance of links
    textView.attributedText = attributedString;
    textView.delegate = self;
    
    - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
        if ([[URL scheme] isEqualToString:@"username"]) {
            NSString *username = [URL host]; 
            // do something with this username
            // ...
            return NO;
        }
        return YES; // let the system open this URL
    }
    
    import Foundation
    
    @objc protocol TappableLabelDelegate {
        optional func tappableLabel(tabbableLabel: TappableLabel, didTapUrl: NSURL, atRange: NSRange)
    }
    
    /// Represent a label with attributed text inside.
    /// We can add a correspondence between a range of the attributed string an a link (URL)
    /// By default, link will be open on the external browser @see 'openLinkOnExternalBrowser'
    
    class TappableLabel: UILabel {
    
        // MARK: - Public properties -
    
        var links: NSMutableDictionary = [:]
        var openLinkOnExternalBrowser = true
        var delegate: TappableLabelDelegate?
    
        // MARK: - Constructors -
    
        override func awakeFromNib() {
            super.awakeFromNib()
            self.enableInteraction()
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.enableInteraction()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        private func enableInteraction() {
            self.userInteractionEnabled = true
            self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector("didTapOnLabel:")))
        }
    
        // MARK: - Public methods -
    
        /**
        Add correspondence between a range and a link.
    
        - parameter url:   url.
        - parameter range: range on which couple url.
        */
        func addLink(url url: String, atRange range: NSRange) {
            self.links[url] = range
        }
    
        // MARK: - Public properties -
    
        /**
        Action rised on user interaction on label.
    
        - parameter tapGesture: gesture.
        */
        func didTapOnLabel(tapGesture: UITapGestureRecognizer) {
            let labelSize = self.bounds.size;
    
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: CGSizeZero)
            let textStorage = NSTextStorage(attributedString: self.attributedText!)
    
            // configure textContainer for the label
            textContainer.lineFragmentPadding = 0
            textContainer.lineBreakMode = self.lineBreakMode
            textContainer.maximumNumberOfLines = self.numberOfLines
            textContainer.size = labelSize;
    
            // configure layoutManager and textStorage
            layoutManager.addTextContainer(textContainer)
            textStorage.addLayoutManager(layoutManager)
    
            // find the tapped character location and compare it to the specified range
            let locationOfTouchInLabel = tapGesture.locationInView(self)
    
            let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
            let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
            let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
                locationOfTouchInLabel.y - textContainerOffset.y)
            let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer,
                inTextContainer:textContainer,
                fractionOfDistanceBetweenInsertionPoints: nil)
    
            for (url, value) in self.links {
                if let range = value as? NSRange {
                    if NSLocationInRange(indexOfCharacter, range) {
                        let url = NSURL(string: url as! String)!
                        if self.openLinkOnExternalBrowser {
                            UIApplication.sharedApplication().openURL(url)
                        }
                        self.delegate?.tappableLabel?(self, didTapUrl: url, atRange: range)
                    }
                }
            }
        }
    
    }
    
    - (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange TapGesture:(UIGestureRecognizer*) gesture{
        NSParameterAssert(label != nil);
    
        // create instances of NSLayoutManager, NSTextContainer and NSTextStorage
        NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
        NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];
    
        // configure layoutManager and textStorage
        [textStorage addLayoutManager:layoutManager];
    
        // configure textContainer for the label
        NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(label.frame.size.width, label.frame.size.height)];
    
        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 = [gesture locationInView:label];
        [layoutManager addTextContainer:textContainer]; //(move here, not sure it that matter that calling this line after textContainer is set
    
        NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInLabel
                                                               inTextContainer:textContainer
                                      fractionOfDistanceBetweenInsertionPoints:nil];
        if (NSLocationInRange(indexOfCharacter, targetRange)) {
            return YES;
        } else {
            return NO;
        }
    }
    
    extension UITapGestureRecognizer {
    
        func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
            // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: CGSize.zero)
            let textStorage = NSTextStorage(attributedString: label.attributedText!)
    
            // Configure layoutManager and textStorage
            layoutManager.addTextContainer(textContainer)
            textStorage.addLayoutManager(layoutManager)
    
            // Configure textContainer
            textContainer.lineFragmentPadding = 0.0
            textContainer.lineBreakMode = label.lineBreakMode
            textContainer.maximumNumberOfLines = label.numberOfLines
            let labelSize = label.bounds.size
            textContainer.size = labelSize
    
            // Find the tapped character location and compare it to the specified range
            let locationOfTouchInLabel = self.location(in: label)
            let textBoundingBox = layoutManager.usedRect(for: textContainer)
            let textContainerOffset = CGPoint(
                x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y
            )
            let locationOfTouchInTextContainer = CGPoint(
                x: locationOfTouchInLabel.x - textContainerOffset.x,
                y: locationOfTouchInLabel.y - textContainerOffset.y
            )
            let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    
            return NSLocationInRange(indexOfCharacter, targetRange)
        }
    
    }
    
    @IBAction func tapLabel(gesture: UITapGestureRecognizer) {
        if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange1) {
            print("Tapped targetRange1")
        } else if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange2) {
            print("Tapped targetRange2")
        } else {
            print("Tapped none")
        }
    }
    
    class TapabbleLabel: UILabel {
    
    let layoutManager = NSLayoutManager()
    let textContainer = NSTextContainer(size: CGSize.zero)
    var textStorage = NSTextStorage() {
        didSet {
            textStorage.addLayoutManager(layoutManager)
        }
    }
    
    var onCharacterTapped: ((label: UILabel, characterIndex: Int) -> Void)?
    
    let tapGesture = UITapGestureRecognizer()
    
    override var attributedText: NSAttributedString? {
        didSet {
            if let attributedText = attributedText {
                textStorage = NSTextStorage(attributedString: attributedText)
            } else {
                textStorage = NSTextStorage()
            }
        }
    }
    
    override var lineBreakMode: NSLineBreakMode {
        didSet {
            textContainer.lineBreakMode = lineBreakMode
        }
    }
    
    override var numberOfLines: Int {
        didSet {
            textContainer.maximumNumberOfLines = numberOfLines
        }
    }
    
    /**
     Creates a new view with the passed coder.
    
     :param: aDecoder The a decoder
    
     :returns: the created new view.
     */
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }
    
    /**
     Creates a new view with the passed frame.
    
     :param: frame The frame
    
     :returns: the created new view.
     */
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUp()
    }
    
    /**
     Sets up the view.
     */
    func setUp() {
        userInteractionEnabled = true
        layoutManager.addTextContainer(textContainer)
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = lineBreakMode
        textContainer.maximumNumberOfLines = numberOfLines
        tapGesture.addTarget(self, action: #selector(TapabbleLabel.labelTapped(_:)))
        addGestureRecognizer(tapGesture)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        textContainer.size = bounds.size
    }
    
    func labelTapped(gesture: UITapGestureRecognizer) {
        guard gesture.state == .Ended else {
            return
        }
    
        let locationOfTouch = gesture.locationInView(gesture.view)
        let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
        let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2 - textBoundingBox.minX,
                                          y: (bounds.height - textBoundingBox.height) / 2 - textBoundingBox.minY)        
        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouch.x - textContainerOffset.x,
                                                     y: locationOfTouch.y - textContainerOffset.y)
        let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer,
                                                                    inTextContainer: textContainer,
                                                                    fractionOfDistanceBetweenInsertionPoints: nil)
    
        onCharacterTapped?(label: self, characterIndex: indexOfCharacter)
    }
    }
    
    let label = TapabbleLabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(label)
    view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[view]-|",
                                                   options: [], metrics: nil, views: ["view" : label]))
    view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[view]-|",
                                                   options: [], metrics: nil, views: ["view" : label]))
    
    let attributedString = NSMutableAttributedString(string: "String with a link", attributes: nil)
    let linkRange = NSMakeRange(14, 4); // for the word "link" in the string above
    
    let linkAttributes: [String : AnyObject] = [
        NSForegroundColorAttributeName : UIColor.blueColor(), NSUnderlineStyleAttributeName : NSUnderlineStyle.StyleSingle.rawValue,
        NSLinkAttributeName: "http://www.apple.com"]
    attributedString.setAttributes(linkAttributes, range:linkRange)
    
    label.attributedText = attributedString
    
    label.onCharacterTapped = { label, characterIndex in
        if let attribute = label.attributedText?.attribute(NSLinkAttributeName, atIndex: characterIndex, effectiveRange: nil) as? String,
            let url = NSURL(string: attribute) {
            UIApplication.sharedApplication().openURL(url)
        }
    }
    
    @implementation UILabel (Support)
    
    - (BOOL)openTappedLinkAtLocation:(CGPoint)location {
      CGSize labelSize = self.bounds.size;
    
      NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
      textContainer.lineFragmentPadding = 0.0;
      textContainer.lineBreakMode = self.lineBreakMode;
      textContainer.maximumNumberOfLines = self.numberOfLines;
      textContainer.size = labelSize;
    
      NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init];
      [layoutManager addTextContainer:textContainer];
    
      NSTextStorage* textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText];
      [textStorage addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, textStorage.length)];
      [textStorage addLayoutManager:layoutManager];
    
      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(location.x - textContainerOffset.x, location.y - textContainerOffset.y);
      NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nullptr];
      if (indexOfCharacter >= 0) {
        NSURL* url = [textStorage attribute:NSLinkAttributeName atIndex:indexOfCharacter effectiveRange:nullptr];
        if (url) {
          [[UIApplication sharedApplication] openURL:url];
          return YES;
        }
      }
      return NO;
    }
    
    @end
    
    @interface UILabel (GSBClickableLinks) <UIGestureRecognizerDelegate>
    @property BOOL enableLinks;
    @end
    
    #import <objc/runtime.h>
    static const void *INDEX;
    static const void *TAP;
    
    @implementation UILabel (GSBClickableLinks)
    
    - (void)setEnableLinks:(BOOL)enableLinks
    {
        UITapGestureRecognizer *tap = objc_getAssociatedObject(self, &TAP); // retreive tap
        if (enableLinks && !tap) { // add a gestureRegonzier to the UILabel to detect taps
            tap = [UITapGestureRecognizer.alloc initWithTarget:self action:@selector(openLink)];
            tap.delegate = self;
            [self addGestureRecognizer:tap];
            objc_setAssociatedObject(self, &TAP, tap, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // save tap
        }
        self.userInteractionEnabled = enableLinks; // note - when false UILAbel wont receive taps, hence disable links
    }
    
    - (BOOL)enableLinks
    {
        return (BOOL)objc_getAssociatedObject(self, &TAP); // ie tap != nil
    }
    
    // First check whether user tapped on a link within the attributedText of the label.
    // If so, then the our label's gestureRecogizer will subsequently fire, and open the corresponding NSLinkAttributeName.
    // If not, then the tap will get passed along, eg to the enclosing UITableViewCell...
    // Note: save which character in the attributedText was clicked so that we dont have to redo everything again in openLink.
    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
    {
        if (gestureRecognizer != objc_getAssociatedObject(self, &TAP)) return YES; // dont block other gestures (eg swipe)
    
        // Re-layout the attributedText to find out what was tapped
        NSTextContainer *textContainer = [NSTextContainer.alloc initWithSize:self.frame.size];
        textContainer.lineFragmentPadding = 0;
        textContainer.maximumNumberOfLines = self.numberOfLines;
        textContainer.lineBreakMode = self.lineBreakMode;
        NSLayoutManager *layoutManager = NSLayoutManager.new;
        [layoutManager addTextContainer:textContainer];
        NSTextStorage *textStorage = [NSTextStorage.alloc initWithAttributedString:self.attributedText];
        [textStorage addLayoutManager:layoutManager];
    
        NSUInteger index = [layoutManager characterIndexForPoint:[gestureRecognizer locationInView:self]
                                                 inTextContainer:textContainer
                        fractionOfDistanceBetweenInsertionPoints:NULL];
        objc_setAssociatedObject(self, &INDEX, @(index), OBJC_ASSOCIATION_RETAIN_NONATOMIC); // save index
    
        return (BOOL)[self.attributedText attribute:NSLinkAttributeName atIndex:index effectiveRange:NULL]; // tapped on part of a link?
    }
    
    - (void)openLink
    {
        NSUInteger index = [objc_getAssociatedObject(self, &INDEX) unsignedIntegerValue]; // retrieve index
        NSURL *url = [self.attributedText attribute:NSLinkAttributeName atIndex:index effectiveRange:NULL];
        if (url && [UIApplication.sharedApplication canOpenURL:url]) [UIApplication.sharedApplication openURL:url];
    }
    
    @end 
    
    NSURL *myURL = [NSURL URLWithString:@"http://www.google.com"];
    NSMutableAttributedString *myString = [NSMutableAttributedString.alloc initWithString:@"This string has a clickable link: "];
    [myString appendAttributedString:[NSAttributedString.alloc initWithString:@"click here" attributes:@{NSLinkAttributeName:myURL}]];
    ...
    myLabel.attributedText = myString;
    myLabel.enableLinks = YES; // yes, that's all! :-)
    
    extension UITapGestureRecognizer {
    
        func didTapAttributedTextInButton(button: UIButton, inRange targetRange: NSRange) -> Bool {
            guard let label = button.titleLabel else { return false }
            return didTapAttributedTextInLabel(label, inRange: targetRange)
        }
    
        func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
            // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: CGSize.zero)
            let textStorage = NSTextStorage(attributedString: label.attributedText!)
    
            // Configure layoutManager and textStorage
            layoutManager.addTextContainer(textContainer)
            textStorage.addLayoutManager(layoutManager)
    
            // Configure textContainer
            textContainer.lineFragmentPadding = 0.0
            textContainer.lineBreakMode = label.lineBreakMode
            textContainer.maximumNumberOfLines = label.numberOfLines
            let labelSize = label.bounds.size
            textContainer.size = labelSize
    
            // Find the tapped character location and compare it to the specified range
            let locationOfTouchInLabel = self.locationInView(label)
            let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
            let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                                  (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
            let locationOfTouchInTextContainer = CGPointMake((locationOfTouchInLabel.x - textContainerOffset.x),
                                                             0 );
            // Adjust for multiple lines of text
            let lineModifier = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
            let rightMostFirstLinePoint = CGPointMake(labelSize.width, 0)
            let charsPerLine = layoutManager.characterIndexForPoint(rightMostFirstLinePoint, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    
            let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
            let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine)
    
            return NSLocationInRange(adjustedRange, targetRange)
        }
    
    }
    
    textView1.attributer =
        "1. ".red
        .append("This is the first test. ").green
        .append("Click on ").black
        .append("evict.nl").makeInteract { _ in
            UIApplication.shared.open(URL(string: "http://evict.nl")!, options: [:], completionHandler: { completed in })
        }.underline
        .append(" for testing links. ").black
        .append("Next test").underline.makeInteract { _ in
            print("NEXT")
        }
        .all.font(UIFont(name: "SourceSansPro-Regular", size: 16))
        .setLinkColor(UIColor.purple) 
    
    textView1.attributer = "@test: What #hashtags do we have in @evermeer #AtributedTextView library"
        .matchHashtags.underline
        .matchMentions
        .makeInteract { link in
            UIApplication.shared.open(URL(string: "https://twitter.com\(link.replacingOccurrences(of: "@", with: ""))")!, options: [:], completionHandler: { completed in })
        }
    
        //****Make sure the textview 'Selectable' = checked, and 'Editable = Unchecked'
    
    import UIKit
    
    class ViewController: UIViewController, UITextViewDelegate {
    
        @IBOutlet var theNewTextView: UITextView!
        override func viewDidLoad() {
            super.viewDidLoad()
    
            //****textview = Selectable = checked, and Editable = Unchecked
    
            theNewTextView.delegate = self
    
            let theString = NSMutableAttributedString(string: "Agree to Terms")
            let theRange = theString.mutableString.range(of: "Terms")
    
            theString.addAttribute(NSLinkAttributeName, value: "ContactUs://", range: theRange)
    
            let theAttribute = [NSForegroundColorAttributeName: UIColor.blue, NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue] as [String : Any]
    
            theNewTextView.linkTextAttributes = theAttribute
    
         theNewTextView.attributedText = theString             
    
    theString.setAttributes(theAttribute, range: theRange)
    
        }
    
        func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
    
            if (URL.scheme?.hasPrefix("ContactUs://"))! {
    
                return false //interaction not allowed
            }
    
            //*** Set storyboard id same as VC name
            self.navigationController!.pushViewController((self.storyboard?.instantiateViewController(withIdentifier: "TheLastViewController"))! as UIViewController, animated: true)
    
            return true
        }
    
    }
    
    extension UITapGestureRecognizer {
        func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
            guard let attrString = label.attributedText else {
                return false
            }
    
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: .zero)
            let textStorage = NSTextStorage(attributedString: attrString)
    
            layoutManager.addTextContainer(textContainer)
            textStorage.addLayoutManager(layoutManager)
    
            textContainer.lineFragmentPadding = 0
            textContainer.lineBreakMode = label.lineBreakMode
            textContainer.maximumNumberOfLines = label.numberOfLines
            let labelSize = label.bounds.size
            textContainer.size = labelSize
    
            let locationOfTouchInLabel = self.location(in: label)
            let textBoundingBox = layoutManager.usedRect(for: textContainer)
            let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
            let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
            let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
            return NSLocationInRange(indexOfCharacter, targetRange)
        }
    }
    
    lblTermsOfUse.isUserInteractionEnabled = true
    lblTermsOfUse.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapOnLabel(_:))))
    
    @objc func handleTapOnLabel(_ recognizer: UITapGestureRecognizer) {
        guard let text = lblAgreeToTerms.attributedText?.string else {
            return
        }
    
        if let range = text.range(of: NSLocalizedString("_onboarding_terms", comment: "terms")),
            recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
            goToTermsAndConditions()
        } else if let range = text.range(of: NSLocalizedString("_onboarding_privacy", comment: "privacy")),
            recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
            goToPrivacyPolicy()
        }
    }
    
    public class LinkLabel: UILabel {
        private var storage: NSTextStorage?
        private let textContainer = NSTextContainer()
        private let layoutManager = NSLayoutManager()
        private var selectedBackgroundView = UIView()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            textContainer.lineFragmentPadding = 0
            layoutManager.addTextContainer(textContainer)
            textContainer.layoutManager = layoutManager
            isUserInteractionEnabled = true
            selectedBackgroundView.isHidden = true
            selectedBackgroundView.backgroundColor = UIColor(white: 0, alpha: 0.3333)
            selectedBackgroundView.layer.cornerRadius = 4
            addSubview(selectedBackgroundView)
        }
    
        public required convenience init(coder: NSCoder) {
            self.init(frame: .zero)
        }
    
        public override func layoutSubviews() {
            super.layoutSubviews()
            textContainer.size = frame.size
        }
    
        public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesBegan(touches, with: event)
            setLink(for: touches)
        }
    
        public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesMoved(touches, with: event)
            setLink(for: touches)
        }
    
        private func setLink(for touches: Set<UITouch>) {
            if let pt = touches.first?.location(in: self), let (characterRange, _) = link(at: pt) {
                let glyphRange = layoutManager.glyphRange(forCharacterRange: characterRange, actualCharacterRange: nil)
                selectedBackgroundView.frame = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer).insetBy(dx: -3, dy: -3)
                selectedBackgroundView.isHidden = false
            } else {
                selectedBackgroundView.isHidden = true
            }
        }
    
        public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesCancelled(touches, with: event)
            selectedBackgroundView.isHidden = true
        }
    
        public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesEnded(touches, with: event)
            selectedBackgroundView.isHidden = true
    
            if let pt = touches.first?.location(in: self), let (_, url) = link(at: pt) {
                UIApplication.shared.open(url)
            }
        }
    
        private func link(at point: CGPoint) -> (NSRange, URL)? {
            let touchedGlyph = layoutManager.glyphIndex(for: point, in: textContainer)
            let touchedChar = layoutManager.characterIndexForGlyph(at: touchedGlyph)
            var range = NSRange()
            let attrs = attributedText!.attributes(at: touchedChar, effectiveRange: &range)
            if let urlstr = attrs[.link] as? String {
                return (range, URL(string: urlstr)!)
            } else {
                return nil
            }
        }
    
        public override var attributedText: NSAttributedString? {
            didSet {
                textContainer.maximumNumberOfLines = numberOfLines
                textContainer.lineBreakMode = lineBreakMode
                if let txt = attributedText {
                    storage = NSTextStorage(attributedString: txt)
                    storage!.addLayoutManager(layoutManager)
                    layoutManager.textStorage = storage
                    textContainer.size = frame.size
                }
            }
        }
    }
    
    func didTapAttributedTextInLabel(gesture: UITapGestureRecognizer, inRange targetRange: NSRange) -> Bool {
    
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: CGSize.zero)
            guard let strAttributedText = self.attributedText else {
                return false
            }
    
            let textStorage = NSTextStorage(attributedString: strAttributedText)
    
            // Configure layoutManager and textStorage
            layoutManager.addTextContainer(textContainer)
            textStorage.addLayoutManager(layoutManager)
    
            // Configure textContainer
            textContainer.lineFragmentPadding = Constants.lineFragmentPadding
            textContainer.lineBreakMode = self.lineBreakMode
            textContainer.maximumNumberOfLines = self.numberOfLines
            let labelSize = self.bounds.size
            textContainer.size = CGSize(width: labelSize.width, height: CGFloat.greatestFiniteMagnitude)
    
            // Find the tapped character location and compare it to the specified range
            let locationOfTouchInLabel = gesture.location(in: self)
    
            let xCordLocationOfTouchInTextContainer = locationOfTouchInLabel.x
            let yCordLocationOfTouchInTextContainer = locationOfTouchInLabel.y
            let locOfTouch = CGPoint(x: xCordLocationOfTouchInTextContainer ,
                                     y: yCordLocationOfTouchInTextContainer)
    
            let indexOfCharacter = layoutManager.characterIndex(for: locOfTouch, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    
            guard let strLabel = text else {
                return false
            }
    
            let charCountOfLabel = strLabel.count
    
            if indexOfCharacter < (charCountOfLabel - 1) {
                return NSLocationInRange(indexOfCharacter, targetRange)
            } else {
                return false
            }
        }
    
    let text = yourLabel.text
    let termsRange = (text as NSString).range(of: fullString)
    if yourLabel.didTapAttributedTextInLabel(gesture: UITapGestureRecognizer, inRange: termsRange) {
                showCorrespondingViewController()
            }
    
    import Foundation
    
    class AELinkedClickableUILabel: UILabel {
    
        typealias YourCompletion = () -> Void
    
        var linkedRange: NSRange!
        var completion: YourCompletion?
    
        @objc func linkClicked(sender: UITapGestureRecognizer){
    
            if let completionBlock = completion {
    
                let textView = UITextView(frame: self.frame)
                textView.text = self.text
                textView.attributedText = self.attributedText
                let index = textView.layoutManager.characterIndex(for: sender.location(in: self),
                                                                  in: textView.textContainer,
                                                                  fractionOfDistanceBetweenInsertionPoints: nil)
    
                if linkedRange.lowerBound <= index && linkedRange.upperBound >= index {
    
                    completionBlock()
                }
            }
        }
    
    /**
     *  This method will be used to set an attributed text specifying the linked text with a
     *  handler when the link is clicked
     */
        public func setLinkedTextWithHandler(text:String, link: String, handler: @escaping ()->()) -> Bool {
    
            let attributextText = NSMutableAttributedString(string: text)
            let foundRange = attributextText.mutableString.range(of: link)
    
            if foundRange.location != NSNotFound {
                self.linkedRange = foundRange
                self.completion = handler
                attributextText.addAttribute(NSAttributedStringKey.link, value: text, range: foundRange)
                self.isUserInteractionEnabled = true
                self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(linkClicked(sender:))))
                return true
            }
            return false
        }
    }
    
    button.setLinkedTextWithHandler(text: "This website (stackoverflow.com) is awesome", link: "stackoverflow.com") 
    {
        // show popup or open to link
    }
    
    import UIKit
    
    public protocol TapableLabelDelegate: NSObjectProtocol {
       func tapableLabel(_ label: TapableLabel, didTapUrl url: String, atRange range: NSRange)
    }
    
    public class TapableLabel: UILabel {
    
    private var links: [String: NSRange] = [:]
    private(set) var layoutManager = NSLayoutManager()
    private(set) var textContainer = NSTextContainer(size: CGSize.zero)
    private(set) var textStorage = NSTextStorage() {
        didSet {
            textStorage.addLayoutManager(layoutManager)
        }
    }
    
    public weak var delegate: TapableLabelDelegate?
    
    public override var attributedText: NSAttributedString? {
        didSet {
            if let attributedText = attributedText {
                textStorage = NSTextStorage(attributedString: attributedText)
            } else {
                textStorage = NSTextStorage()
                links = [:]
            }
        }
    }
    
    public override var lineBreakMode: NSLineBreakMode {
        didSet {
            textContainer.lineBreakMode = lineBreakMode
        }
    }
    
    public override var numberOfLines: Int {
        didSet {
            textContainer.maximumNumberOfLines = numberOfLines
        }
    }
    
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
    
    public override func layoutSubviews() {
        super.layoutSubviews()
        textContainer.size = bounds.size
    }
    
    
    /// addLinks
    ///
    /// - Parameters:
    ///   - text: text of link
    ///   - url: link url string
    public func addLink(_ text: String, withURL url: String) {
        guard let theText = attributedText?.string as? NSString else {
            return
        }
    
        let range = theText.range(of: text)
    
        guard range.location !=  NSNotFound else {
            return
        }
    
        links[url] = range
    }
    
    private func setup() {
        isUserInteractionEnabled = true
        layoutManager.addTextContainer(textContainer)
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = lineBreakMode
        textContainer.maximumNumberOfLines  = numberOfLines
    }
    
    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let locationOfTouch = touches.first?.location(in: self) else {
            return
        }
    
        textContainer.size = bounds.size
        let indexOfCharacter = layoutManager.glyphIndex(for: locationOfTouch, in: textContainer)
    
        for (urlString, range) in links {
            if NSLocationInRange(indexOfCharacter, range), let url = URL(string: urlString) {
                delegate?.tapableLabel(self, didTapUrl: urlString, atRange: range)
            }
        }
    }}
    
    func htmlAttributedString(fontSize: CGFloat = 17.0) -> NSAttributedString? {
                let fontName = UIFont.systemFont(ofSize: fontSize).fontName
                let string = self.appending(String(format: "<style>body{font-family: '%@'; font-size:%fpx;}</style>", fontName, fontSize))
                guard let data = string.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
    
                guard let html = try? NSMutableAttributedString (
                    data: data,
                    options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
                    documentAttributes: nil) else { return nil }
                return html
            }
    
    extension UITapGestureRecognizer {
    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
        guard let attrString = label.attributedText else {
            return false
        }
    
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: .zero)
        let textStorage = NSTextStorage(attributedString: attrString)
    
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)
    
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines
        let labelSize = label.bounds.size
        textContainer.size = labelSize
    
        let locationOfTouchInLabel = self.location(in: label)
        let textBoundingBox = layoutManager.usedRect(for: textContainer)
        let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        return NSLocationInRange(indexOfCharacter, targetRange)
    }
    
    var listurl : [String] = []
        var listURLRange : [NSRange] = []
    
        fun findLinksAndRange(attributeString : NSAttributeString){
            notification.enumerateAttribute(NSAttributedStringKey.link , in: NSMakeRange(0, notification.length), options: [.longestEffectiveRangeNotRequired]) { value, range, isStop in
                        if let value = value {
                            print("\(value) found at \(range.location)")
                            let stringValue = "\(value)"
                            listurl.append(stringValue)
                            listURLRange.append(range)
                        }
                    }
    
                westlandNotifcationLabel.addGestureRecognizer(UITapGestureRecognizer(target : self, action: #selector(handleTapOnLabel(_:))))
    
        }
    
    @objc func handleTapOnLabel(_ recognizer: UITapGestureRecognizer) {
            for index in 0..<listURLRange.count{
                if recognizer.didTapAttributedTextInLabel(label: westlandNotifcationLabel, inRange: listURLRange[index]) {
                    goToWebsite(url : listurl[index])
                }
            }
        }
    
        func goToWebsite(url : String){
            if let websiteUrl = URL(string: url){
                if #available(iOS 10, *) {
                    UIApplication.shared.open(websiteUrl, options: [:],
                                              completionHandler: {
                                                (success) in
                                                print("Open \(websiteUrl): \(success)")
                    })
                } else {
                    let success = UIApplication.shared.openURL(websiteUrl)
                    print("Open \(websiteUrl): \(success)")
                }
            }
        }
    
    extension UILabel {
    
        func setOptimalFontSize(maxFontSize:CGFloat,text:String){
            let width = self.bounds.size.width
    
            var font_size:CGFloat = maxFontSize //Set the maximum font size.
            var stringSize = NSString(string: text).size(withAttributes: [.font : self.font.withSize(font_size)])
            while(stringSize.width > width){
                font_size = font_size - 1
                stringSize = NSString(string: text).size(withAttributes: [.font : self.font.withSize(font_size)])
            }
    
            self.font = self.font.withSize(font_size)//Forcefully change font to match what it would be graphically.
        }
    }
    
    <Label>.setOptimalFontSize(maxFontSize: 36.0, text: formula)
    
    extension UITapGestureRecognizer {
    
        func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
            // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: CGSize.zero)
    
            let mutableAttribString = NSMutableAttributedString(attributedString: label.attributedText!)
            mutableAttribString.addAttributes([NSAttributedString.Key.font: label.font!], range: NSRange(location: 0, length: label.attributedText!.length))
    
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineSpacing = 6
            paragraphStyle.lineBreakMode = .byTruncatingTail
            paragraphStyle.alignment = .center
            mutableAttribString.addAttributes([.paragraphStyle: paragraphStyle], range: NSMakeRange(0, mutableAttribString.string.count))
    
            let textStorage = NSTextStorage(attributedString: mutableAttribString)
    
            // Configure textContainer
            textContainer.lineFragmentPadding = 0.0
            textContainer.lineBreakMode = label.lineBreakMode
            textContainer.maximumNumberOfLines = label.numberOfLines
    
            // Configure layoutManager and textStorage
            layoutManager.addTextContainer(textContainer)
    
            textStorage.addLayoutManager(layoutManager)
    
            let labelSize = label.bounds.size
    
            textContainer.size = labelSize
    
            // Find the tapped character location and compare it to the specified range
            let locationOfTouchInLabel = self.location(in: label)
    
            let textBoundingBox = layoutManager.usedRect(for: textContainer)
            //let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                                  //(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
            let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
    
            //let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
                                                            // locationOfTouchInLabel.y - textContainerOffset.y);
            let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
    
            let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
            print("IndexOfCharacter=",indexOfCharacter)
    
            print("TargetRange=",targetRange)
            return NSLocationInRange(indexOfCharacter, targetRange)
        }
    
    }
    
    let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineSpacing = 6
            paragraphStyle.lineBreakMode = .byTruncatingTail
            paragraphStyle.alignment = .center
            mutableAttribString.addAttributes([.paragraphStyle: paragraphStyle], range: NSMakeRange(0, mutableAttribString.string.count))
    
    <Label>.addGestureRecognizer(UITapGestureRecognizer(target:self, action: #selector(tapLabel(gesture:))))
    
    @IBAction func tapLabel(gesture: UITapGestureRecognizer) {
            guard let text = <Label>.attributedText?.string else {
                return
            }
    
            let click_range = text.range(of: "(α/β)")
    
            if gesture.didTapAttributedTextInLabel(label: <Label>, inRange: NSRange(click_range!, in: text)) {
               print("Tapped a/b")
            }else {
               print("Tapped none")
            }
        }
    
    import UIKit
    
    
    extension UITapGestureRecognizer {
        
        func didTapAttributedString(_ string: String, in label: UILabel) -> Bool {
            
            guard let text = label.text else {
                
                return false
            }
            
            let range = (text as NSString).range(of: string)
            return self.didTapAttributedText(label: label, inRange: range)
        }
        
        private func didTapAttributedText(label: UILabel, inRange targetRange: NSRange) -> Bool {
            
            guard let attributedText = label.attributedText else {
                
                assertionFailure("attributedText must be set")
                return false
            }
            
            let textContainer = createTextContainer(for: label)
            
            let layoutManager = NSLayoutManager()
            layoutManager.addTextContainer(textContainer)
            
            let textStorage = NSTextStorage(attributedString: attributedText)
            if let font = label.font {
                
                textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, attributedText.length))
            }
            textStorage.addLayoutManager(layoutManager)
            
            let locationOfTouchInLabel = location(in: label)
            let textBoundingBox = layoutManager.usedRect(for: textContainer)
            let alignmentOffset = aligmentOffset(for: label)
            
            let xOffset = ((label.bounds.size.width - textBoundingBox.size.width) * alignmentOffset) - textBoundingBox.origin.x
            let yOffset = ((label.bounds.size.height - textBoundingBox.size.height) * alignmentOffset) - textBoundingBox.origin.y
            let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - xOffset, y: locationOfTouchInLabel.y - yOffset)
            
            let characterTapped = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
            
            let lineTapped = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
            let rightMostPointInLineTapped = CGPoint(x: label.bounds.size.width, y: label.font.lineHeight * CGFloat(lineTapped))
            let charsInLineTapped = layoutManager.characterIndex(for: rightMostPointInLineTapped, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
            
            return characterTapped < charsInLineTapped ? targetRange.contains(characterTapped) : false
        }
        
        private func createTextContainer(for label: UILabel) -> NSTextContainer {
            
            let textContainer = NSTextContainer(size: label.bounds.size)
            textContainer.lineFragmentPadding = 0.0
            textContainer.lineBreakMode = label.lineBreakMode
            textContainer.maximumNumberOfLines = label.numberOfLines
            return textContainer
        }
        
        private func aligmentOffset(for label: UILabel) -> CGFloat {
            
            switch label.textAlignment {
                
            case .left, .natural, .justified:
                
                return 0.0
            case .center:
                
                return 0.5
            case .right:
                
                return 1.0
                
                @unknown default:
                
                return 0.0
            }
        }
    }
    
    class ViewController: UIViewController {
        
        @IBOutlet var label : UILabel!
        
        let selectableString1 = "consectetur"
        let selectableString2 = "cupidatat"
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let text = "Lorem ipsum dolor sit amet, \(selectableString1) adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat \(selectableString2) non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
            label.attributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: text))
            
            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(labelTapped))
            label.addGestureRecognizer(tapGesture)
            label.isUserInteractionEnabled = true
        }
        
        @objc func labelTapped(gesture: UITapGestureRecognizer) {
            
            if gesture.didTapAttributedString(selectableString1, in: label) {
                
                print("\(selectableString1) tapped")
            } else if gesture.didTapAttributedString(selectableString2, in: label) {
                
                print("\(selectableString2) tapped")
            } else {
                
                print("Text tapped")
            }
        }
    }
    
    class LinkTextView: UITextView, UITextViewDelegate {
    
    typealias Links = [String: String]
    
    typealias OnLinkTap = (URL) -> Bool
    
    var onLinkTap: OnLinkTap?
    
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        isEditable = false
        isSelectable = true
        isScrollEnabled = false //to have own size and behave like a label   
        delegate = self
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    func addLinks(_ links: Links) {
        guard attributedText.length > 0  else {
            return
        }
        let mText = NSMutableAttributedString(attributedString: attributedText)
        
        for (linkText, urlString) in links {
            if linkText.count > 0 {
                let linkRange = mText.mutableString.range(of: linkText)
                mText.addAttribute(.link, value: urlString, range: linkRange)
            }
        }
        attributedText = mText
    }
    
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
        return onLinkTap?(URL) ?? true
    }
    
    // to disable text selection
    func textViewDidChangeSelection(_ textView: UITextView) {
        textView.selectedTextRange = nil
    }
    
        let linkTextView = LinkTextView()
        let tu = "Terms of Use"
        let pp = "Privacy Policy"
        linkTextView.text = "Please read the Some Company \(tu) and \(pp)"
        linkTextView.addLinks([
            tu: "https://some.com/tu",
            pp: "https://some.com/pp"
        ])
        linkTextView.onLinkTap = { url in
            print("url: \(url)")
            return true
        }