Attributted String link not opening - ios

I need to set a couple of links on the click of an attributed string in a label/textview(UILabel is preferred). I used the following code for setting the links.(multiple links in same label).
NSMutableAttributedString *firstAttributedString = [[NSMutableAttributedString alloc] initWithString:#"By tapping 'Yes', you agree to the " ];
NSMutableAttributedString *secondAttributedString = [[NSMutableAttributedString alloc] initWithString:#"Terms of Use "];
[firstAttributedString appendAttributedString:secondAttributedString];
NSMutableAttributedString *thirdAttributedString = [[NSMutableAttributedString alloc] initWithString:#"and "];
[firstAttributedString appendAttributedString:thirdAttributedString];
NSMutableAttributedString *fourthAttributedString = [[NSMutableAttributedString alloc] initWithString:#"Privacy Policy "];
[firstAttributedString appendAttributedString:fourthAttributedString];
NSMutableAttributedString *fifthAttributedString = [[NSMutableAttributedString alloc] initWithString:#"of example"];
[firstAttributedString appendAttributedString:fifthAttributedString];
NSURL *URL = [NSURL URLWithString: #"example://termsOfUse"];
[secondAttributedString addAttribute: NSLinkAttributeName value: URL range: NSMakeRange(0, secondAttributedString.length)];
[alertTextView setUserInteractionEnabled: YES];
alertTextView.editable=NO;
alertTextView.attributedText = firstAttributedString;
alertTextView.dataDetectorTypes = UIDataDetectorTypeLink;
I have set my info.plist to respond to the url scheme call, but the method in the
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
delegate is not triggering. So I am assuming the click is not getting triggered. What am I doing wrong here? What can I do to solve this?

Upon further review of your sample code, you are trying to modify secondAttributedString after it has been appended to firstAttributedString. This will not produce the results you want, as NSAttributedStrings are not like Arrays.
Think of appending strings like appending a copy of whatever you pass in.
Try adding the link attributes before appending secondAttributedString to the first one.
NSMutableAttributedString *firstAttributedString = [[NSMutableAttributedString alloc] initWithString:#"By tapping 'Yes', you agree to the " ];
NSMutableAttributedString *secondAttributedString = [[NSMutableAttributedString alloc] initWithString:#"Terms of Use "];
NSURL *URL = [NSURL URLWithString: #"example://termsOfUse"];
[secondAttributedString addAttribute: NSLinkAttributeName value: URL range: NSMakeRange(0, secondAttributedString.length)];
[firstAttributedString appendAttributedString:secondAttributedString];
NSMutableAttributedString *thirdAttributedString = [[NSMutableAttributedString alloc] initWithString:#"and "];
[firstAttributedString appendAttributedString:thirdAttributedString];
NSMutableAttributedString *fourthAttributedString = [[NSMutableAttributedString alloc] initWithString:#"Privacy Policy "];
[firstAttributedString appendAttributedString:fourthAttributedString];
NSMutableAttributedString *fifthAttributedString = [[NSMutableAttributedString alloc] initWithString:#"of example"];
[firstAttributedString appendAttributedString:fifthAttributedString];
[alertTextView setUserInteractionEnabled: YES];
alertTextView.editable=NO;
alertTextView.attributedText = firstAttributedString;
Make sure your UITextView has the following properties set:
textView.editable = NO;
textView.dataDetectorTypes = UIDataDetectorTypeAll;
textView.delegate = self; // assuming `self` is the ViewController
You should instead implement the following delegate method:
textView:shouldInteractWithURL:inRange:
and return NO and handle the action yourself.

Related

Make the text hyperlink or clickable in UITextView

I am having the UITextView and displaying text in it from three tags(message, titleUrl and url). What i need is that i want to make the text of "titleUrl" clickable to open the value of "url" in web view. I managed to open the link directly from url, but i need to open the link by clicking "titleUrl". I have tried to achieve the following from this code.
[self buildAgreeTextViewFromString:NSLocalizedString(#"I agree to the #<ts>terms of service# and #<pp>privacy policy#",
#"PLEASE NOTE: please translate \"terms of service\" and \"privacy policy\" as well, and leave the #<ts># and #<pp># around your translations just as in the English version of this message.")];
But i am not getting in this how to modify this to achieve the functionality. I want to enter the value have in string, don't have the static text to enter. Can anyone guide me to handle this?
Update:
NSString *message = [NSString stringWithFormat:#"%#\n ", tempStr1];
NSString *message1 = [NSString stringWithFormat:#"\n#<pp>%##", titlStr1];
NSString *localizedString = NSLocalizedString(message1, nil);
NSRange ppRange = [localizedString rangeOfString:NSLocalizedString(message1, nil) options:NSCaseInsensitiveSearch];
NSURL *ppURL = [NSURL URLWithString:strUrl];
NSDictionary *attribute1 = #{NSForegroundColorAttributeName: [UIColor whiteColor],
NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue" size:15.0],
};
NSMutableAttributedString *newAttString = [[NSMutableAttributedString alloc] initWithString:message attributes:attribute1];
//
NSMutableAttributedString *finalMessage = [[NSMutableAttributedString alloc] initWithString:localizedString];
[finalMessage beginEditing];
[finalMessage addAttributes:attribute1 range:ppRange];
[finalMessage addAttribute:NSLinkAttributeName value:ppURL range:ppRange];
[finalMessage endEditing];
[newAttString appendAttributedString:finalMessage];
self.txtView.attributedText = newAttString;
This is as simple as using an NSMutableAttributedString. Note: This is not the only way, this can be done with searching for ranges etc, this is just a simple implementation to get you in the right direction since you do have the static message, because your localizing all of them, which means you have the static english form of it.
NSString *tosString = #"Terms of Service";
NSString *ppString = #"Privacy Policy";
NSString *message = [NSString stringWithFormat:#"I agree to the #<ts>%## and #<pp>%##", tosString, ppString];
NSString *localizedString = NSLocalizedString(message, nil);
NSRange tosRange = [localizedString rangeOfString:NSLocalizedString(tosString, nil) options:NSCaseInsensitiveSearch];
NSRange ppRange = [localizedString rangeOfString:NSLocalizedString(ppString, nil) options:NSCaseInsensitiveSearch];
NSURL *tosURL = [NSURL URLWithString:#"http://toslink.com"];
NSURL *ppURL = [NSURL URLWithString:#"http://pplink.com"];
NSMutableAttributedString *finalMessage = [[NSMutableAttributedString alloc] initWithString:localizedString];
[finalMessage beginEditing];
[finalMessage addAttribute:NSLinkAttributeName value:tosURL range:tosRange];
[finalMessage addAttribute:NSLinkAttributeName value:ppURL range:ppRange];
[finalMessage endEditing];
self.yourTextView.attributedText = finalMessage;
Swift 3.0
in your view did load...
let tosString = "Terms of Service"
let ppString = "Privacy Policy"
let message = "By logging in, you agree to our (tosString) and that you have read our (ppString)"
let localizedString = NSMutableAttributedString(string: message)
let tosRange = localizedString.mutableString.range(of: tosString)
let ppRange = localizedString.mutableString.range(of: ppString)
let tosURL = URL(string: "http://toslink.com")!
let ppURL = URL(string: "http://pplink.com")!
localizedString.addAttribute(NSLinkAttributeName, value: tosURL, range: tosRange)
localizedString.addAttribute(NSLinkAttributeName, value: ppURL, range: ppRange)
demoTextView.delegate = self
demoTextView.isSelectable = true
demoTextView.isUserInteractionEnabled = true
localizedString.endEditing()
self.demoTextView.attributedText = localizedString
and using textview delegate method
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
// Handle your Control
return true
}
Look into using an attributed string with NSLinkAttributeName.

String and attributions problems

Ive been messing around with this without proper results. My code is as follows
NSMutableAttributedString *nameString = [[NSMutableAttributedString alloc]initWithString:((User *)appDelegate.users[self.currentUserIndex]).name];
[nameString addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"Helvetica-Bold" size:12.0] range:NSMakeRange(0, nameString.length)];
NSString *adviceString = [[NSString alloc] initWithFormat:#"%#, remember be extra aware of the \n cat today. The dog index is 4, dogcatcher \n suggests you should at minimum wear \n a dog and apply cat...", [nameString string]];
So, it doesn't bold the username as desired. I tried using NSAttributedString for adviceString but then I can't use the initWithFormat: method and list the variables after the string, and I don't see any NSAttributedString equivalent. I wanted to you NSAttributedString instead of NSMutableAttributedString, but it doesn't seem to recognise the addAttribute:value:fontWithName:range method. Can anybody help me out? Any help much appreciated.
That's for sure, you need to use NSAttributedString also for adviceString to get attributed.
The code goes like this, if you need to use stringWithFormat:
NSString *fullName = #"Anoop Kumar Vaidya";
NSMutableAttributedString *attributedName = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"Hello %#", fullName]
attributes:#{NSFontAttributeName : [UIFont boldSystemFontOfSize:[UIFont systemFontSize]]}];
In the attributes you can add few more desired/required attributes.
EDIT 2:
You may like to use some methods as :
-(NSMutableAttributedString *)normalString:(NSString *)string{
return [[NSMutableAttributedString alloc] initWithString:string
attributes:#{}];
}
-(NSMutableAttributedString *)boldString:(NSString *)string{
return [[NSMutableAttributedString alloc] initWithString:string
attributes:#{NSFontAttributeName : [UIFont boldSystemFontOfSize:[UIFont systemFontSize]]
}];
}
And then use them as per your requirement:
NSMutableAttributedString *attributedName = [self boldString:fullName];
[attributedName appendAttributedString:[self normalString:#" is creating one"]];
[attributedName appendAttributedString:[self boldString:#" bold"]];
[attributedName appendAttributedString:[self normalString:#" string."]];

Why does this NSMutableAttributedString addAttribute work only if I use a mutableCopy

I have the following code :
NSMutableAttributedString *attrS = [[NSMutableAttributedString alloc] initWithString:#"• Get Tested Son"];
NSMutableAttributedString *boldS = [[NSMutableAttributedString alloc] initWithString:#"Son"];
[boldS addAttribute:NSFontAttributeName value:SOMEBOLDFONT range:NSMakeRange(0, boldS.length)];
[attrS replaceCharactersInRange:[attrS.string rangeOfString:boldS.string]
withAttributedString:boldS];
As you can see, I want to bold the Son part. This does not work if I do the above statements but only works if I do :
[[attrS mutableCopy] replaceCharactersInRange:[attrS.string rangeOfString:boldS.string]
withAttributedString:boldS];
What might be the reason for that?
addAttribute works regardless of whether you take a mutableCopy. Your question is based on a false assumption. It therefore has no answer.
Run this:
NSMutableAttributedString *attrS = [[NSMutableAttributedString alloc] initWithString:#"• Get Tested Son"];
NSMutableAttributedString *boldS = [[NSMutableAttributedString alloc] initWithString:#"Son"];
UIFont *someBoldFont = [UIFont fontWithName:#"Arial" size:23.0f];
[boldS addAttribute:NSFontAttributeName value:someBoldFont range:NSMakeRange(0, boldS.length)];
NSMutableAttributedString *attrSCopy = [attrS mutableCopy];
[attrS replaceCharactersInRange:[attrS.string rangeOfString:boldS.string]
withAttributedString:boldS];
[attrSCopy replaceCharactersInRange:[attrS.string rangeOfString:boldS.string]
withAttributedString:boldS];
NSLog(#"%#", [attrS isEqual:attrSCopy] ? #"equal" : #"different");
It will output equal. Comment out the replaceCharactersInRange: for either attrS or attrSCopy and it will output different.

Code crashing during initialization of NSMutableAttributedString

I need to initialize NsmutableAttributedString with a string productDesc, but the code crashes in the line
attrStrInfoLabel= [[NSMutableAttributedString alloc] initWithString:productDesc];
with error [NSConcreteMutableAttributedString _encodingCantBeStoredInEightBitCFString].
Please advice my code is
NSMutableAttributedString *attrStrInfoLabel;
NSMutableString *productDesc;
productDesc = [NSMutableString stringWithFormat:#"PRODUCT DESCRIPTION:%#", [productDescription objectAtIndex:i]];
attrStrInfoLabel= [[NSMutableAttributedString alloc] initWithString:productDesc];
Try out using NSAttributedString instead of NSMutableString.
Take a look at below Code Sample.
NSMutableAttributedString *attrStrInfoLabel = [[NSMutableAttributedString alloc] init];
NSAttributedString *productDesc = [[NSAttributedString alloc] initWithString:[NSMutableString stringWithFormat:#"PRODUCT DESCRIPTION:%#",[productDescription objectAtIndex:i]];
[attrStrInfoLabel appendAttributedString:productDesc];
Meanwhile check the data in productDescription too. Put some check whether is !nil and has count>0.
The code you posted does not cause the error, but I am going to assume you have an NSLog() call in the following line:
NSLog(#"%#", attrStrInfoLabel);
which should be changed to:
NSLog(#"%#", [attrStrInfoLabel string]);
Initialise it and then append.
NSMutableString *str = [[NSMutableString alloc] init];
[str appendString:YOUR_STRING];

Core Text in UITableviewCell's content overlapping and repeating and superimpose on the other cells

I am using Core Text to add text to UITableviewCell's content but arabic content seems to be overlapping and repeating itself as I scroll and superimpose on the other cells.
I am also using other elements on the page which appear just fine and are not repeating . Just the Core Text seems to be repeating.
I cant figure out why .
Here is my code:
- (CTFontRef)newCustomFontWithName:(NSString *)aFontName
ofType:(NSString *)type
attributes:(NSDictionary *)attributes {
NSString *fontPath = [[NSBundle mainBundle] pathForResource:aFontName ofType:type];
NSData *data = [[NSData alloc] initWithContentsOfFile:fontPath];
CGDataProviderRef fontProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
CGFontRef cgFont = CGFontCreateWithDataProvider(fontProvider);
CGDataProviderRelease(fontProvider);
CTFontDescriptorRef fontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)attributes);
CTFontRef font = CTFontCreateWithGraphicsFont(cgFont, 0, NULL, fontDescriptor);
CFRelease(fontDescriptor);
CGFontRelease(cgFont);
return font;
}
- (CATextLayer *)customCATextLayer:(NSString *)textString {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:24.f], (NSString *)kCTFontSizeAttribute,
[NSNumber numberWithInt:1], (NSString *)kCTLigatureAttributeName,
nil];
CTFontRef font = [self newCustomFontWithName:#"me_quranKer6"
ofType:#"ttf"
attributes:attributes];
CATextLayer *normalTextLayer = [[CATextLayer alloc] init];
normalTextLayer.font = font;
normalTextLayer.string = textString;
normalTextLayer.wrapped = YES;
normalTextLayer.foregroundColor = [[UIColor blackColor] CGColor];
normalTextLayer.fontSize = 24.f;
normalTextLayer.alignmentMode = kCAAlignmentCenter;
normalTextLayer.frame = CGRectMake(0.f, 10.f, 320.f, 32.f);
CFRelease(font);
return normalTextLayer;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
QuranVersesViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"verseCell"];
Verse *verse = [self.fetchedResultsController objectAtIndexPath:indexPath];
//English Content starts
NSMutableAttributedString * englishAttributedString;
if (!englishAttributedString)
englishAttributedString = [[NSMutableAttributedString alloc] initWithString:#""];
NSMutableAttributedString * englishSubtitleAttributedString;
NSMutableAttributedString * englishVerseAttributedString;
if (!englishVerseAttributedString)
englishVerseAttributedString = [[NSMutableAttributedString alloc] initWithString:verse.english_version];
NSMutableAttributedString * englishFootnoteAttributedString;
if (!englishFootnoteAttributedString)
englishFootnoteAttributedString = [[NSMutableAttributedString alloc] init];
NSString *englishString = #"";
if(verse.subtitle.length>0)
{
NSMutableParagraphStyle *mutParaStyle=[[NSMutableParagraphStyle alloc] init];
[mutParaStyle setAlignment:NSTextAlignmentCenter];
englishSubtitleAttributedString = [[NSMutableAttributedString alloc] initWithString:verse.subtitle];
[englishSubtitleAttributedString addAttributes:[NSDictionary dictionaryWithObject:mutParaStyle
forKey:NSParagraphStyleAttributeName]
range:NSMakeRange(0,[[englishSubtitleAttributedString string] length])];
[englishAttributedString appendAttributedString:englishSubtitleAttributedString];
[englishAttributedString addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"Arial" size:30] range:NSRangeFromString(verse.subtitle)];
NSLog(#"text us %#", englishAttributedString);
}// englishString = [englishString stringByAppendingString:[NSString stringWithFormat:#"%#\n\n", verse.subtitle]];
[englishAttributedString appendAttributedString:englishVerseAttributedString];
englishString = [englishString stringByAppendingString:[NSString stringWithFormat:#"[%#:%#] %#\n", verse.whichSura.sura_no, verse.verse_no, verse.english_version]];
if(verse.footnote.length>0)
englishString = [englishString stringByAppendingString: [NSString stringWithFormat:#"\n%#\n", verse.footnote]];
englishString = [englishString stringByReplacingOccurrencesOfString:#"“" withString:#"\"" ];
englishString = [englishString stringByReplacingOccurrencesOfString:#"_" withString:#"\n" ];
cell.quranVerseEnglishTextView.attributedText = englishAttributedString;
[cell.quranVerseEnglishTextView autoResizeWithMaxWidth:MAX_TEXT_WIDTH];
cell.quranVerseEnglishTextView.backgroundColor = [UIColor clearColor];
//English Content starts
//Arabic Content
CATextLayer *arabicTextLayer = [self customCATextLayer:verse.arabic_version];
[cell.arabicView.layer addSublayer:arabicTextLayer];
return cell;
}
I was facing the same problem until I read up about NSAttributedStrings (made available in iOS 6) on this tutorial here.
The following code will solve your issue:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:info.text attributes:#{ NSFontAttributeName : [UIFont fontWithName:#"Scheherazade" size:32], NSLigatureAttributeName: #2}];
cell.textLabel.attributedText = attributedString;
Out of curiosity, would I be correct to say that you opted to use CoreText because of difficulties in rendering embedded arabic fonts? I ventured the guess because I was attempting to use a similar method as you have done in your code when faced with that exact problem for a Quran app that I'm currently developing. If this so then I can confirm that using NSAttributedString also solves the problem. If you notice in the code above I've also set the NSLigatureAttributeName to 2 which according to the official Apple Class Reference Documentation means 'all ligatures'. Just note that this is something that I'm currently testing and I have yet to see the effects of this but I know that ligatures is a common problem in the rendering of some arabic fonts on certain platforms.
While on the subject, another common problem you may be facing is the line-spacing of arabic text and the slight overlapping of multi-line text and I've found that NSAttributedString can also be a good solution when used together with NSParagraphStyle (Hooray again for NSAttributedString!). Simply modify the above code as below:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:info.text attributes:#{ NSFontAttributeName : [UIFont fontWithName:#"Scheherazade" size:32], NSLigatureAttributeName: #2}];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:20];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [info.text length])];
cell.textLabel.attributedText = attributedString;
Hope this helps you or anyone else out there!
EDIT - Adding this helpful post on Common Mistakes With Adding Custom Fonts to Your iOS App for reference as a "checklist" when adding custom fonts on iOS.
Actually fixed the issue myself by adding the following line in cellforRowAtIndexPath:
if (cell == nil)
{
cell = [[QuranVersesViewCell alloc] init];
.....
and also did all the initialization and setting only when the cell was nil. And MOST importantly tagged the view layer and set the text for only the matching tagged view...

Resources