objective c - iOS UITextView or UILabel with clickable links to actions -
this question has answer here:
i want make uilabel
or uitextview
text 2 clickable links in it. not links webpages want link 2 links actions uibutton
. examples i've seen links webviews dont want that. well, text translated in other languages positions have dynamic.
want make this:
i needed solve exact same problem: similar text 2 links in it, on multiple lines, , needing able translated in language (including different word orders, etc). solved it, let me share how did it.
initially thinking should create attributed text , map tap's touch location regions within text. while think doable, think it's complicated approach.
this ended doing instead:
summary:
- have basic custom markup in english message can parse out different pieces
- instruct translators leave markup in , translate rest
- have uiview can serve container of message
- break english message in pieces separate regular text clickable text
- for each piece create uilabel on container uiview
- for clickable pieces, set styling, allow user interaction , create tap gesture recognizer
- do basic bookkeeping place words across lines
detail:
in view controller's viewdidload
placed this:
[self buildagreetextviewfromstring:nslocalizedstring(@"i agree #<ts>terms of service# , #<pp>privacy policy#", @"please note: please translate \"terms of service\" , \"privacy policy\" well, , leave #<ts># , #<pp># around translations in english version of message.")];
i'm calling method build message. note markup came with. can of course invent own, key mark ends of each clickable region because span on multiple words.
here's method puts message -- see below. first break english message on #
character (or rather @"#"
string). way each piece need create label separately. loop on them , basic markup of <ts>
, <pp>
detect pieces links what. if chunk of text i'm working link, style bit , set tap gesture recogniser it. strip out markup characters of course. think easy way it.
note subtleties how handle spaces: take spaces (localised) string. if there no spaces (chinese, japanese), there won't spaces between chunks either. if there spaces, automatically space out chunks needed (e.g. english). when have place word @ start of next line though, need make sure strip of white space prefix text, because otherwise doesn't align properly.
- (void)buildagreetextviewfromstring:(nsstring *)localizedstring { // 1. split localized string on # sign: nsarray *localizedstringpieces = [localizedstring componentsseparatedbystring:@"#"]; // 2. loop through pieces: nsuinteger msgchunkcount = localizedstringpieces ? localizedstringpieces.count : 0; cgpoint wordlocation = cgpointmake(0.0, 0.0); (nsuinteger = 0; < msgchunkcount; i++) { nsstring *chunk = [localizedstringpieces objectatindex:i]; if ([chunk isequaltostring:@""]) { continue; // skip loop if chunk empty } // 3. determine type of word is: bool istermsofservicelink = [chunk hasprefix:@"<ts>"]; bool isprivacypolicylink = [chunk hasprefix:@"<pp>"]; bool islink = (bool)(istermsofservicelink || isprivacypolicylink); // 4. create label, styling dependent on whether it's link: uilabel *label = [[uilabel alloc] init]; label.font = [uifont systemfontofsize:15.0f]; label.text = chunk; label.userinteractionenabled = islink; if (islink) { label.textcolor = [uicolor colorwithred:110/255.0f green:181/255.0f blue:229/255.0f alpha:1.0]; label.highlightedtextcolor = [uicolor yellowcolor]; // 5. set tap gesture clickable text: sel selectoraction = istermsofservicelink ? @selector(tapontermsofservicelink:) : @selector(taponprivacypolicylink:); uitapgesturerecognizer *tapgesture = [[uitapgesturerecognizer alloc] initwithtarget:self action:selectoraction]; [label addgesturerecognizer:tapgesture]; // trim markup characters label: if (istermsofservicelink) label.text = [label.text stringbyreplacingoccurrencesofstring:@"<ts>" withstring:@""]; if (isprivacypolicylink) label.text = [label.text stringbyreplacingoccurrencesofstring:@"<pp>" withstring:@""]; } else { label.textcolor = [uicolor whitecolor]; } // 6. lay out labels forms complete sentence again: // if word doesn't fit @ end of line, move next // line , make sure leading spaces stripped off aligns nicely: [label sizetofit]; if (self.agreetextcontainerview.frame.size.width < wordlocation.x + label.bounds.size.width) { wordlocation.x = 0.0; // move word way left... wordlocation.y += label.frame.size.height; // ...on next line // , trim of leading white space: nsrange startingwhitespacerange = [label.text rangeofstring:@"^\\s*" options:nsregularexpressionsearch]; if (startingwhitespacerange.location == 0) { label.text = [label.text stringbyreplacingcharactersinrange:startingwhitespacerange withstring:@""]; [label sizetofit]; } } // set location label: label.frame = cgrectmake(wordlocation.x, wordlocation.y, label.frame.size.width, label.frame.size.height); // show label: [self.agreetextcontainerview addsubview:label]; // update horizontal position next word: wordlocation.x += label.frame.size.width; } }
and here methods handle detected taps on links.
- (void)tapontermsofservicelink:(uitapgesturerecognizer *)tapgesture { if (tapgesture.state == uigesturerecognizerstateended) { nslog(@"user tapped on terms of service link"); } } - (void)taponprivacypolicylink:(uitapgesturerecognizer *)tapgesture { if (tapgesture.state == uigesturerecognizerstateended) { nslog(@"user tapped on privacy policy link"); } }
hope helps. i'm sure there smarter , more elegant ways this, able come , works nicely.
here's how looks in app:
good luck! :-)
erik
Comments
Post a Comment