1

I have this UILabel I agree to the Terms and Policy. I wish to make Terms and Policy clickable respectively. I have managed to add attribute to Terms and Policy to make them Blue and Underline. But how to make them clickable respectively. When Terms are clicked, I wish to push to a new VC that loads up the webView and same goes for Policy. I am familiar with using segue to push to another VC but how to initialise the process when Terms or Policy is clicked>

Edited

#import "ViewController.h"

@interface ViewController ()
@end

@implementation ViewController
@synthesize label;
@synthesize layoutManager;
@synthesize textContainer;
@synthesize textStorage;

- (void)viewDidLoad {
    [super viewDidLoad];

    NSString *fullString = @"I agree to the Terms and Policy";
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:fullString];

    //For underline
    [attributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:[fullString rangeOfString:@"Terms"]];
[attributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:[fullString rangeOfString:@"Policy"]];
    //For Blue Colour
    [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0] range:[fullString rangeOfString:@"Terms"]];
    [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0] range:[fullString rangeOfString:@"Policy"]];

    // Setting attributed string to textview
    label.attributedText = attributedString;

    // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];

    //=====What does this part do, Do I really need it?======
    // Configure layoutManager and textStorage
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];

    // Configure textContainer
    textContainer.lineFragmentPadding = 0.0;
    textContainer.lineBreakMode = label.lineBreakMode;
    textContainer.maximumNumberOfLines = label.numberOfLines;
    //======================================================

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

- (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.textContainerfractionOfDistanceBetweenInsertionPoints:nil];
    NSRange termsLinkRange = NSMakeRange(15, 5); // it's better to save the range somewhere when it was originally used for marking link in attributed string
    NSRange policyLinkRange = NSMakeRange(25, 6);

    //=========THE FOLLOWING PART IS NOT WORKING =====================
    if (NSLocationInRange(indexOfCharacter, termsLinkRange)) {

       NSLog(@"This is terms");
       [self performSegueWithIdentifier:@"Terms" sender:self];    

    }else if(NSLocationInRange(indexOfCharacter, policyLinkRange)){

       NSLog(@"This is policy");
       [self performSegueWithIdentifier:@"Policy" sender:self];
    }
 }
 //======================================================================
 @end

I keep getting indexOfCharacter equal to 0

Hanz Cheah
  • 761
  • 5
  • 15
  • 44
  • Take a look at this post https://stackoverflow.com/questions/20541676/ios-uitextview-or-uilabel-with-clickable-links-to-actions. Maybe it's what you want – trungduc Apr 19 '18 at 04:38
  • I did but don't know where or how they load the web view. – Hanz Cheah Apr 19 '18 at 06:22
  • About how to load the web view, you can check RB1509's answer or ask him. I think he know how to do it. – trungduc Apr 19 '18 at 06:24
  • IndexOfCharacter keep returning zero value, any help is much appreciated. – Hanz Cheah Apr 20 '18 at 02:31
  • @HansheungCheah you try to popup a viewcontroller(which contain webview) for terms and policy like many app. right or not? –  Apr 20 '18 at 06:34
  • @HansheungCheah they guys not load webview. –  Apr 20 '18 at 06:41
  • @HansheungCheah [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://google.com/"]]; that work as link and open by default in browser –  Apr 20 '18 at 06:42
  • @RB1509 I can manage to load web view. I just want to differentiate between clicking on Terms and clicking on Policy. How to know when user click on Terms and how to know when click on Policy. if you look at my edited questions, you will understand. – Hanz Cheah Apr 20 '18 at 08:05

3 Answers3

1

You can use a UITextView to achieve this, I had implemented this using that way in previous projects, use this extension to create those links more easily

Code

UITextView extension .h

#import <UIKit/UIKit.h>

@interface UITextView (Link)

- (void)setTextAsLink:(NSString*)text url:(NSURL*)linkurl attributtes:(NSDictionary*)attributes;

@end

UITextView extension .m

#import "UITextView+Link.h"

@implementation UITextView (Link)

    - (void)setTextAsLink:(NSString*)text url:(NSURL*)linkurl attributtes:(NSDictionary*)attributes {

        NSMutableAttributedString * theString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
        NSRange theRange = [theString.mutableString rangeOfString:text];


        if(theRange.location != NSNotFound) {
            [theString addAttribute:NSLinkAttributeName value:linkurl range:theRange];
            self.attributedText = theString;
        }

        self.linkTextAttributes = attributes;
    }

    @end

USING IT

#import "ViewController.h"
#import "UITextView+Link.h"

@interface ViewController () <UITextViewDelegate>
@property (weak, nonatomic) IBOutlet UITextView *textView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.textView.editable = false;
    self.textView.contentInset = UIEdgeInsetsZero;
    self.textView.textContainerInset = UIEdgeInsetsZero;
    self.textView.attributedText = [[NSAttributedString alloc] initWithString:@"I agree to the Terms and Policy" attributes:nil];
    self.textView.delegate = self;
    NSDictionary * linkAttributes = @{NSForegroundColorAttributeName:[UIColor redColor]};

    [self.textView setTextAsLink:@"Terms" url:[NSURL URLWithString:@"terms://"] attributtes:linkAttributes];
    [self.textView setTextAsLink:@"Policy" url:[NSURL URLWithString:@"policy://"] attributtes:linkAttributes];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


#pragma mark -
#pragma mark UITextViewDelegate

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView{
    return false;
}

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction {
    if([URL.absoluteString isEqualToString:@"terms://"]) {
        NSLog(@"TERMS SHOULD BE OPENED");
    }
    if([URL.absoluteString isEqualToString:@"policy://"]) {
        NSLog(@"POLICY SHOULD BE OPENED");
    }
    return false;
}

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
    if([URL.absoluteString isEqualToString:@"terms://"]) {
        NSLog(@"TERMS SHOULD BE OPENED");
    }
    if([URL.absoluteString isEqualToString:@"policy://"]) {
        NSLog(@"POLICY SHOULD BE OPENED");
    }
    return false;
}

@end

enter image description here

Reinier Melian
  • 20,519
  • 3
  • 38
  • 55
  • How do you create `UITextView extension .h` & `UITextView extension .m` How to import `#import "UITextView+Link.h"` Can you elaborate a bit more on how to create all these? Is it a subclass of UITextView? – Hanz Cheah Jul 19 '18 at 09:14
  • @HanzCheah I don't understand what clarifications you need, this is a category, https://code.tutsplus.com/tutorials/objective-c-succinctly-categories-and-extensions--mobile-22016 and how using it, if you have any doubt please let me know – Reinier Melian Jul 19 '18 at 09:22
0

To achieve this, you should use UITextView and make it selectable. Adding attributes to the textview will make the links clickable. So you don't need to add a gesture.

Don't forget to set these properties of textview:

textview.isSelectable = true
textview.isEditable = false
textview.isUserInteractionEnabled = true

Also implement the UITextViewDelegate method:

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
    //open the link here
    if URL.absoluteString == "YOUR_TERMS_URL" {
        //open terms
    } else if URL.absoluteString == "YOUR_PRIVACY_URL" {
        //open privacy
    } 
    return false
}
hardik parmar
  • 853
  • 8
  • 15
  • That's not what the OP is asking, he wants to launch a view controller when the user clicks a link, not directly launch the link itself. – Gruntcakes Apr 18 '18 at 17:12
0

You can add a UITapGestureRecognizer instance to your UILabel.

UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel)];
tapGestureRecognizer.numberOfTapsRequired = 1;
tapGestureRecognizer.delegate =self;
[myLabel addGestureRecognizer:tapGestureRecognizer];
myLabel.userInteractionEnabled = YES;

then as @RB1509 said

- (void)handleTapOnLabel:(UITapGestureRecognizer *)gestureRecognizer {
     UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
     secondVC *VC = [storyboard instantiateViewControllerWithIdentifier:@"secondVC"];
     VC.GetString=@"https://www.vbgov.com/Pages/default.aspx";
     [self presentViewController:VC animated:YES completion:nil];
}

Updated:

so now you need to detect touches on the text

- (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 termsLinkRange = NSMakeRange(15, 5); // it's better to save the range somewhere when it was originally used for marking link in attributed string 
    NSRange policyLinkRange = NSMakeRange(25, 6);
    if (NSLocationInRange(indexOfCharacter, termsLinkRange)) {
       // Open an URL, or handle the tap on the text TERMS
       [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];
    }else if(NSLocationInRange(indexOfCharacter, policyLinkRange)){
       // Open an URL, or handle the tap on the text POLICY
       [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];
    }
}

I agree to the Terms and Policy = terms NSMakeRange(15, 5) and Policy NSMakeRange(25, 6) *Do change the range accordingly.

Vicky_Vignesh
  • 584
  • 2
  • 14