Search code examples
iosobjective-cuilabeluitapgesturerecognizernsmutableattributedstring

Clickable Terms and Policy that loads to up to another VC with webView ObjectiveC


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


Solution

  • 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