NSAttributedString text position - objective-c

I sub-classed a UILabel, so I can use this TitleLabel on several places. This TitleLabel has a custom font. The lineheight is set with an NSAttributedString.
This is the drawTextInRect override method:
- (void)drawTextInRect:(CGRect)rect {
self.text = #"THIS IS A TEST";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:self.text];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.minimumLineHeight = 15.f;
paragraphStyle.maximumLineHeight = 15.f;
[attStr addAttribute:NSBackgroundColorAttributeName value:[UIColor yellowColor] range:NSMakeRange(0,7)];
[attStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, self.text.length)];
[attStr addAttribute:NSFontAttributeName value:[UIFont fontWithName:TITLE_FONT_NAME size:TITLE_FONT_SIZE] range:NSMakeRange(0, self.text.length)];
self.attributedText = attStr;
[super drawTextInRect:rect];
}
The background colors are added for test purposes. As you can see, the yellow background color has the right position. The text THIS IS should be in the yellow background, but is positioned above the background.
TITLE_FONT_SIZE is 15, defined somewhere else.
Anyone knows why this happens?

You are changing the font size, so that if the font is too big the text gets cut. That's normal, change your font size according to the rect size. Make it be something like this:
[attStr addAttribute:NSFontAttributeName value:[UIFont fontWithName:TITLE_FONT_NAME size: MIN(rect.size.height*0.4,rect.size.width*0.4)] range:NSMakeRange(0, self.text.length)];
EDIT
Not sure which rectangle gets passed to the method. There's nothing in the doc, maybe is the frame and not the view bounds. Try this:
[super drawTextInRect: [self convertRect: rect fromView: nil] ];

Related

How to change font in NSTextContainer

My application is setup as so.
Custom UIView inside a ScrollView.
The custom UIView is getting text generated on top of it with this code.
CoreDavening.m draw function
attStorage = [[NSTextStorage alloc]initWithAttributedString:string];
textContainer = [[NSTextContainer alloc]
initWithSize:CGSizeMake(self.frame.size.width, FLT_MAX)];
layoutManager = [[NSLayoutManager alloc]init];
[layoutManager addTextContainer:textContainer];
[attStorage addLayoutManager:layoutManager];
NSRange glyphRange = [layoutManager
glyphRangeForTextContainer:textContainer];
[layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: rect.origin];
In my ViewController.m I'm creating the view like so.
CoreDavening *davenView = [[CoreDavening alloc]initWithFrame:CGRectMake(0, 0,self.view.frame.size.width, [coreDavening heightForStringDrawing])];
davenView.backgroundColor = [UIColor whiteColor];
davenView.layer.borderColor = [[UIColor whiteColor]CGColor];
scrollView.backgroundColor = [UIColor whiteColor];
[scrollView addSubview:davenView];
self.scrollView.contentSize = davenView.frame.size;
Im trying to find a way to 1) change the font size (just size) 2) have the scrollView and CustomView resize appropriately.
How can this be done?
EDIT 1:
Im creating the custom view in my viewController like this.
CoreDavening *coreDavening = [[CoreDavening alloc]init];
davenView = [[CoreDavening alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, [coreDavening heightForStringDrawing ])];
and updating/refreshing the custom view like this.
It get triggered when the slider I have set up changes value.
- (IBAction)sliderChanged:(id)sender {
NSUserDefaults *prefs= [NSUserDefaults standardUserDefaults];
[prefs setInteger:(long)self.Slider.value forKey:#"fontSize"];
[prefs synchronize];
[davenView setNeedsLayout];
}
My custom view controller has the font set up like.
THIS IS IN THE DRAW FUNCTION
attStorage = [[NSTextStorage alloc]initWithAttributedString:string];
[attStorage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:(long)[prefs integerForKey:#"fontSize"]] range:NSMakeRange(0, attStorage.length)];
textContainer = [[NSTextContainer alloc]
initWithSize:CGSizeMake(self.frame.size.width, FLT_MAX)];
layoutManager = [[NSLayoutManager alloc]init];
[layoutManager addTextContainer:textContainer];
[attStorage addLayoutManager:layoutManager];
NSRange glyphRange = [layoutManager
glyphRangeForTextContainer:textContainer];
[layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: rect.origin];
It seems like you have the textStorage associated with the layoutManager already. NSTextStorage is the subclass of NSMutableAttributedString. So, this is the class which is responsible to hold the layout and styling information. How have you created your textStorage ? Besides, the styling, NSTextStorage also has methods to allow editing the contents and have dynamic styling.
You can simply set the attribute to your textStorage if you want to change the font.
[_textStorage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20.0] range:NSMakeRange(0, _textStorage.length)];
NSTextManager class has quite useful methods to find the bounds and glyphs informations. So, you could use method like below to find the bounds for your text,
- (CGRect)boundingRectWithSize:(CGSize)size
{
self.textContainer.size = size;
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
CGRect boundingRect = [self.layoutManager boundingRectForGlyphRange:glyphRange
inTextContainer:self.textContainer];
boundingRect.origin.x = 0;
boundingRect.origin.y = 0;
return boundingRect;
}

NSGradient into NSColor

OK, long story short:
I'm using (embedded into the bundle) FontAwesome
I'm using it as the font in some custom NSButtons
In the NSButton subclass I want to colour them, exactly the way the Xcode tab items are coloured
This is how I'm setting the color (as a simple NSColor):
NSColor *color = [NSColor colorWithCalibratedRed:0.09 green:0.55 blue:0.90 alpha:1.0];
NSMutableAttributedString *colorTitle =
[[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTitle]];
NSRange titleRange = NSMakeRange(0, [colorTitle length]);
[colorTitle addAttribute:NSForegroundColorAttributeName
value:color
range:titleRange];
[self setAttributedTitle:colorTitle];
How can I set it to an NSGradient?
OK, here's the solution, for anyone who may find useful...
Step 1:
Create a category on NSColor, based on the great answer by #Omz. In the code below, you'll see it renamed as colorFromGradient:, solely in order to mix well with the usual Cocoa naming conventions...
Step 2:
Redraw the title with the gradient color
NSColor* gS = [NSColor colorWithCalibratedRed:0.07 green:0.47 blue:0.87 alpha:1.0];
NSColor* gE = [NSColor colorWithCalibratedRed:0.12 green:0.64 blue:0.94 alpha:1.0];
NSGradient* g = [[NSGradient alloc] initWithStartingColor:gE endingColor:gS];
NSColor *color = [NSColor colorFromGradient:g];
NSMutableAttributedString *colorTitle =
[[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTitle]];
NSRange titleRange = NSMakeRange(0, [colorTitle length]);
[colorTitle addAttribute:NSForegroundColorAttributeName
value:color
range:titleRange];
[self setAttributedTitle:colorTitle];
Step 3:
Enjoy the result. :-)

Showing NSAttributedString at the Center With NSView

I have the following function to make an attributed string.
- (NSMutableAttributedString *)makeText:(NSString *)txt : (NSString *)fontname : (CGFloat)tsize :(NSColor *)textColor :(NSColor *)shadowColor :(NSSize)offset {
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = shadowColor;
shadow.shadowOffset = offset;
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 10.0f;
paragraphStyle.alignment = NSCenterTextAlignment;
NSMutableAttributedString *atext = [[NSMutableAttributedString alloc] initWithString:txt];
NSRange range = NSMakeRange(0,txt.length);
// alignment
[atext addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0,[atext length])];
...
...
return atext;
}
If I set this attributed string to NSTextField, the resulting attributed string will be aligned correctly. But if I send it to NSView's subclass, the string will be left-aligned. Is there any way by which I can display this attributed string with correct alignment?
// .h
#interface imageView1 : NSView {
NSAttributedString *aStr;
}
// .m
- (void)drawRect:(NSRect)dirtyRect {
[aStr drawAtPoint:NSMakePoint((self.frame.size.width-aStr.size.width)/2.0f,(self.frame.size.height-aStr.size.height)/2.0f)];
}
Thank you for your help. The OS version is 10.8.
If you draw at a point there are no bounds in relation to which to center. You need to specify a rect. See the docs on drawWithRect:options:. Note the admonition there pointing out that to be in that rect your options will need to be (or include) NSStringDrawingUsesLineFragmentOrigin.

How to increase the font size of a NSAttributedsString in iOS

I am trying to change the font size of an NSAttributedString dynamically. The problem is the string contains different font sizes and properties. so when i change the font size all the content size change to tat value. Not changing accordingly . .. . .
If I understood you correctly this approach should help you: you can enumerate all NSFontAttributeName attributes for your AttributedString and increase the font size by for instance 1. This would give you the following result:
If that's what you want here is the code to achieve this
- (void)viewDidLoad
{
[super viewDidLoad];
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0., 0., 320., 320.)];
self.label.textAlignment = NSTextAlignmentCenter;
self.label.backgroundColor = [UIColor whiteColor];
self.label.numberOfLines = 0.;
NSString *text = #"Small medium large";
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text];
[attributedText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:10] range:NSMakeRange(0, 6)];
[attributedText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:15] range:NSMakeRange(6, 7)];
[attributedText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:25] range:NSMakeRange(13, 5)];
self.label.attributedText = attributedText;
[self.view addSubview:self.label];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(bumpFontSize) userInfo:nil repeats:YES];
}
- (void)bumpFontSize
{
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithAttributedString:self.label.attributedText];
[self.label.attributedText enumerateAttributesInRange:NSMakeRange(0., self.label.text.length) options:NSAttributedStringEnumerationReverse usingBlock:
^(NSDictionary *attributes, NSRange range, BOOL *stop)
{
NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
UIFont *font = mutableAttributes[NSFontAttributeName];
UIFontDescriptor *fontProperties = font.fontDescriptor;
NSNumber *sizeNumber = fontProperties.fontAttributes[UIFontDescriptorSizeAttribute];
[attributedText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize: [sizeNumber floatValue] + 1.] range:range];
}];
self.label.attributedText = attributedText;
}

NSAttributedString drop shadow is cropping on descenders

I am using NSMutableAttributedString in iOS6 to apply kerning and drop shadows within a UILabel subclass. The attributes are being applied properly, however the drop shadow is cropping at at the left edge of the first character and the bottom of descenders ( lowercase p for example ). The label itself has clipsToBounds disabled.
The code...
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[self text]];
NSRange range = NSMakeRange(0, [attributedString length]);
NSNumber *kern = [NSNumber numberWithFloat:0.75f];
[attributedString addAttribute:NSKernAttributeName value:kern range:range];
NSShadow *shadow = [[NSShadow alloc] init];
[shadow setShadowBlurRadius:3.0f];
[shadow setShadowColor:[UIColor darkGrayColor]];
[shadow setShadowOffset:CGSizeMake(0, 2.0f)];
[attributedString addAttribute:NSShadowAttributeName value:shadow range:range];
[self setAttributedText:attributedString];
Its barely noticeable, but once you see it, it's obvious. Have others encountered this bug and is there a work-around?