Cannot get a drop shadow on multiline nsstring - ios

I'm trying to get multiline text to draw with a drop shadow without using deprecated APIs. It works fine for a single line. The relevant code looks like this:
-(void)drawRect:(CGRect)rect
{
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraph.lineBreakMode = NSLineBreakByWordWrapping;
paragraph.alignment = NSTextAlignmentCenter;
UIFont *f = [UIFont systemFontOfSize:20.0];
NSMutableDictionary *attributes = [NSMutableDictionary new];
[attributes setValuesForKeysWithDictionary:#{ NSFontAttributeName : f,
NSParagraphStyleAttributeName : paragraph,
NSForegroundColorAttributeName : [UIColor blueColor] }];
NSShadow * shadow = [NSShadow new];
shadow.shadowOffset = CGSizeMake(4,4);
shadow.shadowColor = [UIColor redColor];
[attributes setValue:shadow forKey:NSShadowAttributeName];
rect.origin.y = 100;
[#"test string on one line" drawInRect:rect withAttributes:attributes];
rect.origin.y = 150;
[#"test string spanning more than one line" drawInRect:rect withAttributes:attributes];
}
and the output looks like this:
I have tested this on iPhone 5 (7.1.2), iPhone 6 (8.0), building with xCode 6. I have also tested it on the iPhone 5 when building with xCode 5.

Some more experimentation, and I discovered that the answer is to use an NSAttributedString.
While this does not show a shadow:
NSString *s = #"test string spanning more than one line"
[s drawInRect:rect withAttributes:attributes]
This does:
NSAttributedString *as = [[NSAttributedString alloc] initWithString:s attributes:attributes];
[as drawInRect:rect];
I don't think this is documented anywhere, would love to hear otherwise.

Related

Have multiple ranges with different attributes on NSAttributedString

I have a multiline UILabel of which I would like to increase the line height, but I also want part of it to be a different color, only the line height works fine. But as soon as I try to change the color for a certain range it just goes back to the stock appearance, no line either..
Anyone a tip? This is done in the content setter.
- (void)setContent:(NSString *)content {
_content = content;
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:self.content];
NSMutableAttributedString *mutableAttrString = [attributedString mutableCopy];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraphStyle setLineSpacing: 5.0f];
NSDictionary *attributes = #{
NSFontAttributeName: [UIFont fontWithName:#"BentonSans-Regular" size:16.0],
NSParagraphStyleAttributeName: paragraphStyle
};
NSDictionary *colorAttributes = #{
NSForegroundColorAttributeName: [UIColor redColor]
};
[mutableAttrString addAttributes:attributes range:NSRangeFromString(self.content)];
[mutableAttrString addAttributes:colorAttributes range:NSMakeRange(4, 8)];
[self.label setAttributedText: mutableAttrString];
}
Thanks!
The NSRangeFromString function expects a string like #"{3,10}". In other words, it expects a string that contains two numbers that specify the starting location and length of the range. I suspect that the content string isn't a string like that.
So this line
[mutableAttrString addAttributes:attributes range:NSRangeFromString(self.content)];
should be
[mutableAttrString addAttributes:attributes range:NSMakeRange(0,mutableAttrString.length)];
in your viewdidLoad Method assign string to self.content :
self.content = #"pass your text ";
// Remove the First line of your method it is not needed
- (void)setContent:(NSString *)content {
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:self.content];
NSMutableAttributedString *mutableAttrString = [attributedString mutableCopy];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraphStyle setLineSpacing: 5.0f];
NSDictionary *attributes = #{
NSFontAttributeName: [UIFont fontWithName:#"BentonSans-Regular" size:16.0],
NSParagraphStyleAttributeName: paragraphStyle
};
NSDictionary *colorAttributes = #{
NSForegroundColorAttributeName: [UIColor redColor]
};
[mutableAttrString addAttributes:attributes range:NSRangeFromString(self.content)];
[mutableAttrString addAttributes:colorAttributes range:NSMakeRange(4, 8)];
[self.label setAttributedText: mutableAttrString];
}

iOS 6 multiline label line spacing

There is a problem with line spacing in UILabel, I am using custom font and when I use smilies there is no space between two lines. which obviously looks not so good. So I used this code for line spacing but app crashes giving the error
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSAttributedString invalid for autoresizing, it must have a single spanning paragraph style (or none) with a non-wrapping lineBreakMode.'
if ([cell.label2 respondsToSelector:#selector(setAttributedText:)])
{
UIFont *font =btMyriadProRegularWithSize14Pt;
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraphStyle setLineSpacing: 22];
NSDictionary *attributes = #{ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle };
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:chatMessage.msgString attributes:attributes];
[cell.label2 setAttributedText: attributedString];
}
else
{
NSString * msg = [NSString stringWithFormat:#"%#: %#",chatMessage.from,chatMessage.msgString];
cell.label2.text = msg;
}
try this
[cell.label2 setAdjustsFontSizeToFitWidth:NO];
maybe even only for iOS6
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { //*
[cell.label2 setAdjustsFontSizeToFitWidth:NO];
}
detect iOS 6 as decribed here: https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/SupportingEarlieriOS.html
Set up Attributable String
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
[paragraph setLineBreakMode:NSLineBreakByWordWrapping];
[paragraph setLineBreakMode:NSLineBreakByTruncatingTail];
self.attrText = [[NSMutableAttributedString alloc] initWithString:text];
[self.attrText addAttribute:NSParagraphStyleAttributeName value:paragraph range:NSMakeRange(0, text.length)];
self.Text = text;

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...

Increasing the font size of first letter in UILabel.text

I would like the first letter of the UILabel to have a different font size from others, a few font size larger. Need some guidance on how to do it. The label has a lot of words.
This is what I have tried:
NSArray * words = [Label.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *firstLetter = [[words objectAtIndex:0] substringToIndex:1];
But got stuck in increasing the size of the NSString. Is there a better way? I am welcome to suggestions and guidance.. Thanks..
EDIT:
[Label setFont:[UIFont fontWithName:#"Arial" size:12.f]];
NSArray * words = [Label.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
UIFont *fontFirst=[UIFont fontWithName:#"Arial" size:15.f];
NSDictionary *attrsDictFirst=[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
NSAttributedString *finalString=[[NSAttributedString alloc] initWithString:[[words objectAtIndex:0] substringToIndex:1] attributes:attrsDictFirst];
To get, for example, this:
Say this:
NSString* s2 = #"Fourscore and seven years ago, our fathers brought forth "
#"upon this continent a new nation, conceived in liberty and dedicated "
#"to the proposition that all men are created equal.";
NSMutableAttributedString* content2 =
[[NSMutableAttributedString alloc]
initWithString:s2
attributes:
#{
NSFontAttributeName:
[UIFont fontWithName:#"HoeflerText-Black" size:16]
}];
[content2 setAttributes:
#{
NSFontAttributeName:[UIFont fontWithName:#"HoeflerText-Black" size:24]
} range:NSMakeRange(0,1)];
[content2 addAttributes:
#{
NSKernAttributeName:#-4
} range:NSMakeRange(0,1)];
NSMutableParagraphStyle* para2 = [NSMutableParagraphStyle new];
para2.headIndent = 10;
para2.firstLineHeadIndent = 10;
para2.tailIndent = -10;
para2.lineBreakMode = NSLineBreakByWordWrapping;
para2.alignment = NSTextAlignmentJustified;
para2.lineHeightMultiple = 1.2;
para2.hyphenationFactor = 1.0;
[content2 addAttribute:NSParagraphStyleAttributeName
value:para2 range:NSMakeRange(0,1)];
Then assign content2 to a label's attributedText.
Need to use NSAttributedString. iOS6.0 onwards
After getting the first letter make put it into attributed string, change its size.
take rest of the string set other size.
UIFont *font=[UIFont fontWithName:#"Arial" size:12.f];
NSDictionary *attrsDict=[NSDictionary dictionaryWithObject:font
forKey:NSFontAttributeName];
NSAttributedString *attribString=[[NSAttributedString alloc] initWithString:[words[0] substringFromIndex:1] attributes:attrsDict];
UIFont *fontFirst=[UIFont fontWithName:#"Arial" size:15.f];
NSDictionary *attrsDictFirst=[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
NSAttributedString *firstString=[[NSAttributedString alloc] initWithString:[attribString subStringToIndex:1] attributes:attrsDictFirst];
[attribString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstString];

How do I use HTML.fromhtml in IOS?

I want to make a NSString notification like this: "You must pay 4.00 dollar.".The money has a red color.In Android ,I can use HTML.formhtml do it.But I don't know how to do it in IOS.Who can help me?
try this code
CATextLayer *aTextLayer_= [[[CATextLayer alloc]init] autorelease];
aTextLayer_.frame = CGRectMake(10, 100, 300, 50);
aTextLayer_.backgroundColor=[UIColor clearColor].CGColor;
aTextLayer_.foregroundColor = [[UIColor redColor] CGColor];
aTextLayer_.alignmentMode=kCAAlignmentCenter;
aTextLayer_.contentsScale = [[UIScreen mainScreen] scale];
aTextLayer_ .wrapped=YES;
[aTextLayer_ setAlignmentMode:kCAAlignmentLeft];
UIFont *smallFont = [UIFont systemFontOfSize:25];
CTFontRef ctSmallFont = CTFontCreateWithName((CFStringRef)smallFont.fontName, smallFont.pointSize, NULL);
UIFont *boldFont = [UIFont systemFontOfSize:25];
CTFontRef ctBoldFont = CTFontCreateWithName((CFStringRef)boldFont.fontName, boldFont.pointSize, NULL);
CGColorRef cgColor = [UIColor greenColor].CGColor;
CGColorRef cgColor1 = [UIColor redColor].CGColor;
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(id)ctSmallFont, (id)kCTFontAttributeName,
cgColor, (id)kCTForegroundColorAttributeName, nil];
NSDictionary *subattributes = [NSDictionary dictionaryWithObjectsAndKeys:
(id)ctBoldFont, (id)kCTFontAttributeName,
cgColor1, (id)kCTForegroundColorAttributeName, nil];
CFRelease(ctBoldFont);
NSString *subString=#"You must pay 4.00 dollar.";
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:subString attributes:attributes];
[attrStr addAttributes:subattributes range:NSMakeRange(13,4)];
aTextLayer_.string=attrStr;
[self.view.layer addSublayer:aTextLayer_];
If you don't want to use a UIWebView your best options is to use a NSAttributedString.
A naive implementation without caring too much about corner cases and error checking would be something like
NSString * string = #"You must pay 4.00 dollar";
NSString * pattern = #"You must pay (.+) dollar";
NSRegularExpression * regex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
NSArray * matches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)];
NSRange priceRange = [matches[0] rangeAtIndex:1];
NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:string];
[attrString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:priceRange];
Note that I assumed that your patter will always be You must pay (.+) dollar. In case it's different simply adjust the regex in order to retrieve the NSRange of the substring that needs to be stylized.
Then you can use the attributed string as a content of anything accepting a NSAttributedString, for instance UILabel has an attributedText property since iOS 6, therefore you could do something like
yourLabel.attributedText = attrString;
you can add html code to UIWebView
[_webView loadHTMLString:#"<p>You must pay <font color=\"red\">4.00</font> dollar. </p> " baseURL:nil];
try this code
-----------------Alternate Option----------------------------
If this text format is going to be fixed (You must pay XXXX.XXX dollar.) better Create custom component
Create class extend UIView
Add UILable with different font color

Resources