Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Tuesday, January 19, 2016

Create tap-able

Create tap-able "links" in the NSAttributedText of a UILabel?


I have been searching this for hours but I've failed. I probably don't even know what I should be looking for.

Many applications have text and in this text are web hyperlinks in rounded rect. When I click them UIWebView opens. What puzzles me is that they often have custom links, for example if words starts with # it is also clickable and the application responds by opening another view. How can I do that? Is it possible with UILabel or do I need UITextView or something else?

Answer by Charles Gamble for Create tap-able "links" in the NSAttributedText of a UILabel?


UITextView supports data-detectors in OS3.0, whereas UILabel doesn't.

If you enable the data-detectors on the UITextView and your text contains URLs, phone numbers, etc. they will appear as links.

Answer by Kendall Helmstetter Gelner for Create tap-able "links" in the NSAttributedText of a UILabel?


For fully custom links, you'll need to use a UIWebView - you can intercept the calls out, so that you can go to some other part of your app instead when a link is pressed.

Answer by Lope for Create tap-able "links" in the NSAttributedText of a UILabel?


FancyLabel is exactly what I needed :)

Answer by Iggy for Create tap-able "links" in the NSAttributedText of a UILabel?


The UIButtonTypeCustom is a clickable label if you don't set any images for it.

Answer by Ankit Goyal for Create tap-able "links" in the NSAttributedText of a UILabel?


    NSString *string = name;      NSError *error = NULL;      NSDataDetector *detector =      [NSDataDetector dataDetectorWithTypes:(NSTextCheckingTypes)NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber                                      error:&error];      NSArray *matches = [detector matchesInString:string                                           options:0                                             range:NSMakeRange(0, [string length])];      for (NSTextCheckingResult *match in matches)      {          if (([match resultType] == NSTextCheckingTypePhoneNumber))          {              NSString *phoneNumber = [match phoneNumber];              NSLog(@" Phone Number is :%@",phoneNumber);              label.enabledTextCheckingTypes = NSTextCheckingTypePhoneNumber;          }            if(([match resultType] == NSTextCheckingTypeLink))          {              NSURL *email = [match URL];              NSLog(@"Email is  :%@",email);              label.enabledTextCheckingTypes = NSTextCheckingTypeLink;          }            if (([match resultType] == NSTextCheckingTypeLink))          {              NSURL *url = [match URL];              NSLog(@"URL is  :%@",url);              label.enabledTextCheckingTypes = NSTextCheckingTypeLink;          }      }        label.text =name;  }  

Answer by Sannny for Create tap-able "links" in the NSAttributedText of a UILabel?


Here is example code to hyperlink UILabel: Source:http://sickprogrammersarea.blogspot.in/2014/03/adding-links-to-uilabel.html

#import "ViewController.h"  #import "TTTAttributedLabel.h"  @interface ViewController ()    @end    @implementation ViewController  {  UITextField *loc;  TTTAttributedLabel *data;  }    - (void)viewDidLoad  {  [super viewDidLoad];  UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(5, 20, 80, 25) ];  [lbl setText:@"Text:"];  [lbl setFont:[UIFont fontWithName:@"Verdana" size:16]];  [lbl setTextColor:[UIColor grayColor]];  loc=[[UITextField alloc] initWithFrame:CGRectMake(4, 20, 300, 30)];  //loc.backgroundColor = [UIColor grayColor];  loc.borderStyle=UITextBorderStyleRoundedRect;  loc.clearButtonMode=UITextFieldViewModeWhileEditing;  //[loc setText:@"Enter Location"];  loc.clearsOnInsertion = YES;  loc.leftView=lbl;  loc.leftViewMode=UITextFieldViewModeAlways;  [loc setDelegate:self];  [self.view addSubview:loc];  [loc setRightViewMode:UITextFieldViewModeAlways];  CGRect frameimg = CGRectMake(110, 70, 70,30);  UIButton *srchButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];  srchButton.frame=frameimg;  [srchButton setTitle:@"Go" forState:UIControlStateNormal];  [srchButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];  srchButton.backgroundColor=[UIColor clearColor];  [srchButton addTarget:self action:@selector(go:) forControlEvents:UIControlEventTouchDown];  [self.view addSubview:srchButton];  data = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(5, 120,self.view.frame.size.width,200) ];  [data setFont:[UIFont fontWithName:@"Verdana" size:16]];  [data setTextColor:[UIColor blackColor]];  data.numberOfLines=0;   data.delegate = self;  data.enabledTextCheckingTypes=NSTextCheckingTypeLink|NSTextCheckingTypePhoneNumber;  [self.view addSubview:data];  }  - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url  {  NSString *val=[[NSString alloc]initWithFormat:@"%@",url];  if ([[url scheme] hasPrefix:@"mailto"]) {            NSLog(@" mail URL Selected : %@",url);      MFMailComposeViewController *comp=[[MFMailComposeViewController alloc]init];      [comp setMailComposeDelegate:self];      if([MFMailComposeViewController canSendMail])      {          NSString *recp=[[val substringToIndex:[val length]] substringFromIndex:7];          NSLog(@"Recept : %@",recp);          [comp setToRecipients:[NSArray arrayWithObjects:recp, nil]];          [comp setSubject:@"From my app"];          [comp setMessageBody:@"Hello bro" isHTML:NO];          [comp setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];          [self presentViewController:comp animated:YES completion:nil];      }    }  else{      [[UIApplication sharedApplication] openURL:[NSURL URLWithString:val]];  }  }  -(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{  if(error)  {      UIAlertView *alrt=[[UIAlertView alloc]initWithTitle:@"Erorr" message:@"Some error occureed" delegate:nil cancelButtonTitle:@"" otherButtonTitles:nil, nil];      [alrt show];      [self dismissViewControllerAnimated:YES completion:nil];  }  else{      [self dismissViewControllerAnimated:YES completion:nil];  }  }    - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber  {  NSLog(@"Phone Number Selected : %@",phoneNumber);  UIDevice *device = [UIDevice currentDevice];  if ([[device model] isEqualToString:@"iPhone"] ) {      [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel:%@",phoneNumber]]];  } else {      UIAlertView *Notpermitted=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"Your device doesn't support this feature." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];      [Notpermitted show];  }  }  -(void)go:(id)sender  {  [data setText:loc.text];  }    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event  {  NSLog(@"Reached");  [loc resignFirstResponder];  }  

Answer by NAlexN for Create tap-able "links" in the NSAttributedText of a UILabel?


In general, if we want to have a clickable link in text displayed by UILabel, we would need to resolve two independent tasks:

  1. Changing the appearance of a portion of the text to look like a link
  2. Detecting and handling touches on the link (opening an URL is a particular case)

The first one is easy. Starting from iOS 6 UILabel supports display of attributed strings. All you need to do is to create and configure an instance of NSMutableAttributedString:

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;  

That's it! The code above makes UILabel to display String with a link

Now we should detect touches on this link. The idea is to catch all taps within UILabel and figure out whether the location of the tap was close enough to the link. To catch touches we can add tap gesture recognizer to the label. Make sure to enable userInteraction for the label, it's turned off by default:

label.userInteractionEnabled = YES;  [label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];   

Now the most sophisticated stuff: finding out whether the tap was on where the link is displayed and not on any other portion of the label. If we had single-lined UILabel, this task could be solved relatively easy by hardcoding the area bounds where the link is displayed, but let's solve this problem more elegantly and for general case - multiline UILabel without preliminary knowledge about the link layout.

One of the approaches is to use capabilities of Text Kit API introduced in iOS 7:

// 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;  

Save created and configured instances of NSLayoutManager, NSTextContainer and NSTextStorage in properties in your class (most likely UIViewController's descendant) - we'll need them in other methods.

Now, each time the label changes its frame, update textContainer's size:

- (void)viewDidLayoutSubviews  {      [super viewDidLayoutSubviews];      self.textContainer.size = self.label.bounds.size;  }  

And finally, detect whether the tap was exactly on the link:

- (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:@"http://stackoverflow.com/"]];      }  }  

Answer by zekel for Create tap-able "links" in the NSAttributedText of a UILabel?


(My answer builds on @NAlexN's excellent answer. I won't duplicate his detailed explanation of each step here.)

I found it most convenient and straightforward to add support for tap-able UILabel text as a category to UITapGestureRecognizer. (You don't have to use UITextView's data detectors, as some answers suggest.)

Add the following method to your UITapGestureRecognizer category:

/**   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;      }  }  

Example Code

// (in your view controller)      // create your label, gesture recognizer, attributed text, and get the range of the "link" in your label  myLabel.userInteractionEnabled = YES;  [myLabel addGestureRecognizer:     [[UITapGestureRecognizer alloc] initWithTarget:self                                              action:@selector(handleTapOnLabel:)]];     // create your attributed text and keep an ivar of your "link" text range  NSAttributedString *plainText;  NSAttributedString *linkText;  plainText = [[NSMutableAttributedString alloc] initWithString:@"Add label links with UITapGestureRecognizer"                                                     attributes:nil];  linkText = [[NSMutableAttributedString alloc] initWithString:@" Learn more..."                                                    attributes:@{                                                        NSForegroundColorAttributeName:[UIColor blueColor]                                                    }];  NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] init];  [attrText appendAttributedString:plainText];  [attrText appendAttributedString:linkText];    // ivar -- keep track of the target range so you can compare in the callback  targetRange = NSMakeRange(plainText.length, linkText.length);  

Gesture Callback

// handle the gesture recognizer callback and call the category method  - (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture {      BOOL didTapLink = [tapGesture didTapAttributedTextInLabel:myLabel                                              inRange:targetRange];      NSLog(@"didTapLink: %d", didTapLink);    }  

Answer by hsusmita for Create tap-able "links" in the NSAttributedText of a UILabel?


I created UILabel subclass named ResponsiveLabel which is based on textkit API introduced in iOS 7. It uses the same approach suggested by NAlexN. It provides flexibility to specify a pattern to search in the text. One can specify styles to be applied to those patterns as well as action to be performed on tapping the patterns.

//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];  

If you want to make a string clickable, you can do this way. This code applies attributes to each occurrence of the string "text".

PatternTapResponder tapResponder = ^(NSString *string) {      NSLog(@"tapped = %@",string);  };    [self.customLabel enableStringDetection:@"text" withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor],                                                                   RLTapResponderAttributeName: tapResponder}];  

Answer by RacZo for Create tap-able "links" in the NSAttributedText of a UILabel?


I had a hard time dealing with this... UILabel with links on it on attributed text... it is just a headache so I ended up using ZSWTappableLabel.

Answer by jjpp for Create tap-able "links" in the NSAttributedText of a UILabel?


Create the class with the following .h and .m files. In the .m file there is the following function

 - (void)linkAtPoint:(CGPoint)location  

Inside this function we will check the ranges of substrings for which we need to give actions. Use your own logic to put your ranges.

And following is the usage of the subclass

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  

following is the .h file

#import     @interface TaggedLabel : UILabel    @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     

following is the .m file

#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  

Answer by Jinghan Wang for Create tap-able "links" in the NSAttributedText of a UILabel?


As I mentioned in this post, here is a light-weighted library I created specially for links in UILabel FRHyperLabel.

To achieve an effect like this:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis blandit eros, sit amet vehicula justo. Nam at urna neque. Maecenas ac sem eu sem porta dictum nec vel tellus.

use code:

//Step 1: Define a normal attributed string for non-link texts  NSString *string = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis blandit eros, sit amet vehicula justo. Nam at urna neque. Maecenas ac sem eu sem porta dictum nec vel tellus.";  NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]};    label.attributedText = [[NSAttributedString alloc]initWithString:string attributes:attributes];      //Step 2: Define a selection handler block  void(^handler)(FRHyperLabel *label, NSString *substring) = ^(FRHyperLabel *label, NSString *substring){      NSLog(@"Selected: %@", substring);  };      //Step 3: Add link substrings  [label setLinksForSubstrings:@[@"Lorem", @"Pellentesque", @"blandit", @"Maecenas"] withLinkHandler:handler];  

Answer by Lukasz Czerwinski for Create tap-able "links" in the NSAttributedText of a UILabel?


I'd strongly recommend using a library that automatically detects URLs in text and converts them to links. Try:

Both are under MIT license.

Answer by black_stallion for Create tap-able "links" in the NSAttributedText of a UILabel?


Old question but if anyone can use a UITextView instead of a UILabel, then it is easy. Standard URLs, phone numbers etc will be automatically detected (and be clickable).

However, if you need custom detection, that is, if you want to be able to call any custom method after a user clicks on a particular word, you need to use NSAttributedStrings with an NSLinkAttributeName attribute that will point to a custom URL scheme(as opposed to having the http url scheme by default). Ray Wenderlich has it covered here

Quoting the code from the aforementioned link:

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;  

To detect those link clicks, implement this:

- (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  }  

PS: Make sure your UITextView is selectable.

Answer by Velthune for Create tap-able "links" in the NSAttributedText of a UILabel?


TAGS #Swift2.0

I take inspiration on - excellent - @NAlexN's answer and I decide to write by myself a wrapper of UILabel.
I also tried TTTAttributedLabel but I can't make it works.

Hope you can appreciate this code, any suggestions are welcome!

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)                  }              }          }      }    }  


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.