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


i have been searching hours i've failed. don't know should looking for.

many applications have text , in text web hyperlinks in rounded rect. when click them uiwebview opens. puzzles me have custom links, example if words starts # clickable , application responds opening view. how can that? possible uilabel or need uitextview or else?

in general, if want have clickable link in text displayed uilabel, need resolve 2 independent tasks:

  1. changing appearance of portion of text link
  2. detecting , handling touches on link (opening url particular case)

the first 1 easy. starting ios 6 uilabel supports display of attributed strings. need create , configure instance of nsmutableattributedstring:

nsmutableattributedstring *attributedstring = [[nsmutableattributedstring alloc] initwithstring:@"string link" attributes:nil]; nsrange linkrange = nsmakerange(14, 4); // word "link" in 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 uilabel label.attributedtext = attributedstring; 

that's it! code above makes uilabel display string link

now should detect touches on link. idea catch taps within uilabel , figure out whether location of tap close enough link. catch touches can add tap gesture recognizer label. make sure enable userinteraction label, it's turned off default:

label.userinteractionenabled = yes; [label addgesturerecognizer:[[uitapgesturerecognizer alloc] initwithtarget:self action:@selector(handletaponlabel:)]];  

now sophisticated stuff: finding out whether tap on link displayed , not on other portion of label. if had single-lined uilabel, task solved relatively easy hardcoding area bounds link displayed, let's solve problem more elegantly , general case - multiline uilabel without preliminary knowledge link layout.

one of approaches use capabilities of text kit api introduced in ios 7:

// create instances of nslayoutmanager, nstextcontainer , nstextstorage nslayoutmanager *layoutmanager = [[nslayoutmanager alloc] init]; nstextcontainer *textcontainer = [[nstextcontainer alloc] initwithsize:cgsizezero]; nstextstorage *textstorage = [[nstextstorage alloc] initwithattributedstring:attributedstring];  // configure layoutmanager , textstorage [layoutmanager addtextcontainer:textcontainer]; [textstorage addlayoutmanager:layoutmanager];  // configure textcontainer textcontainer.linefragmentpadding = 0.0; textcontainer.linebreakmode = label.linebreakmode; textcontainer.maximumnumberoflines = label.numberoflines; 

save created , configured instances of nslayoutmanager, nstextcontainer , nstextstorage in properties in class (most uiviewcontroller's descendant) - we'll need them in other methods.

now, each time label changes frame, update textcontainer's size:

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

and finally, detect whether tap on 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 save range somewhere when used marking link in attributed string     if (nslocationinrange(indexofcharacter, linkrange)) {         // open url, or handle tap on link in other way         [[uiapplication sharedapplication] openurl:[nsurl urlwithstring:@"https://stackoverflow.com/"]];     } } 

Comments

Popular posts from this blog

java.util.scanner - How to read and add only numbers to array from a text file -

rewrite - Trouble with Wordpress multiple custom querystrings -

php - Accessing static methods using newly created $obj or using class Name -