Incorrect font measures - cocoa

On a NSTextField I'm setting a custom font with a size of 140. The text is set to #"28". But as you can clearly see on the image, the text field has plenty of space on top. This only happens with certain type of fonts, not all of them. My question is what information from the font could be affecting the textfield that ends up cropping the text ? (Ascender, Cap Height ?). And if so, can I modify the font file to fix it ?

The baseline will vary between fonts. In addition, there are other metrics that vary. You can work around this problem with NSAttributedString. You could try varying the NSBaselineOffsetAttribute and from within a paragraph setMinimumLineHeight and setMaximumLineHeight. The following is an example. Make sure to create two textField labels and connect their outlets.
self.Label1.stringValue = #"Test Text";
//
// baseline is different for each font!
//
//self.Label2.stringValue = #"Test Text";
NSFont *otherFont = [NSFont fontWithName:#"MarkerFelt-Thin" size:40.0f];
NSNumber *baseline = [[NSNumber alloc] initWithFloat: 5.0f];
NSMutableParagraphStyle *paraStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paraStyle setParagraphSpacingBefore:20.0f];
[paraStyle setMinimumLineHeight:30.0f];
[paraStyle setMaximumLineHeight:50.0f];
NSDictionary *otherFDict = [NSDictionary dictionaryWithObjectsAndKeys: paraStyle, NSParagraphStyleAttributeName,
otherFont, NSFontAttributeName, baseline, NSBaselineOffsetAttributeName, nil];
NSMutableAttributedString *otherText = [[NSMutableAttributedString alloc] initWithString:#"Test Text" attributes:otherFDict];
self.Label2.attributedStringValue = otherText;

Related

Tooltips in NSTextView/NSTextfields

I want to display tooltips over parts of an attributed string, and thought that using an NSToolTipAttributeName attribute on the required ranges would do the trick but I can't get it to work.
I am trying in MACOS, Xcode (9.2)
NSString *fullString = #"Create a new tooltip";
NSString *compareString =#"tooltip";
NSRange tooltipRange = [fullString rangeOfString: compareString];
[senderTextView.textStorage addAttribute:NSToolTipAttributeName
value:#"Custom tooltip"
range:tooltipRange];
[senderTextView.textStorage addAttribute:NSForegroundColorAttributeName
value:[NSColor redColor] range:tooltipRange];
The word "tooltip" is shown in red as expected, but no tooltip appears when I hover over it. What am I missing?
Couple of ways to do this, using the NSToolTipAttributeName in combination with NSLinkAttributeName, e.g.:
NSMutableDictionary* labelAttributes = [NSMutableDictionary new];
labelAttributes[NSToolTipAttributeName] = #"Tooltip";
labelAttributes[NSLinkAttributeName] = #"Link";
NSAttributedString* labelString = [NSAttributedString
attributedString:#"Display"]
withAttributes:labelAttributes];
Being careful not to forget displaysLinkToolTips = YES on the NSTextView.
Alternately, you can use the NSView Tooltips API, which provides:
- (NSToolTipTag)addToolTipRect:(NSRect)rect owner:(id)owner userData:(void *)data;

Centering text in OS X Screensaver

I'm working on implementing a (very) simple screensaver using the Screensaver framework in OS X 10.10. Positioning on the center of the screen and displaying a two-line text works without problem, but setting the alignment to NSCenterTextAlignment somehow doesn't (the text is always displayed left-aligned).
- (void)animateOneFrame
{
// calculate font size based on screen
NSSize size = [self bounds].size;
CGFloat fontsize = size.height / 11;
// set text
NSMutableParagraphStyle *centredStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[centredStyle setAlignment:NSCenterTextAlignment];
NSDictionary *textAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSFont fontWithName:#"Futura" size:fontsize], NSFontAttributeName,
[NSColor orangeColor], NSForegroundColorAttributeName,
centredStyle, NSParagraphStyleAttributeName,
nil];
NSString *theText = #"Simple text spanning\ntwo lines";
// position text on screen
NSRect boundingRect = [theText boundingRectWithSize:size options:0 attributes:textAttributes];
NSPoint point = NSMakePoint((size.width - boundingRect.size.width) / 2.0,
(size.height - boundingRect.size.height) / 2.0);
[theText drawAtPoint: point withAttributes: textAttributes];
}
Any pointers on how to solve this are appreciated.
PS: I know that I don't need to put everything into animateOneFrame but for the moment the goal is to get it working at all:-)

Sometimes wrong size of multiline string

I try to find size of certain string using following code:
NSAttributedString *attributedString = ...// Actually system font, 13 pt.
NSSize size = ...// Actually more than string size
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:size];
[textContainer setLineFragmentPadding:0.0];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager setTypesetterBehavior:NSTypesetterBehavior_10_2_WithCompatibility]; // Setting up different typeSetterBehavior would not fixes the size issue
[layoutManager addTextContainer:textContainer];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
[textStorage addLayoutManager:layoutManager];
// Layout manager performs layout lazily, force it to lay out the text
[layoutManager glyphRangeForTextContainer:textContainer];
NSSize textSize = [layoutManager usedRectForTextContainer:textContainer].size;
And at that point, textSize for some strings! less then string need. For example, for "Doesn't look like a string value." string in returned size can be drawn only "Doesn't look like a string", but for #"Doesn't look like a string value. This is a number." string all ok.
All same happens when I receive string size with:
[attributedString boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin].size
or
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
NSSize textSize = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer].size;
Can anyone explain me why for some string I receive wrong size?
Thanks.
I’m guessing when you create the views that are displaying the text, you’re ignoring that NSTextField and NSTextView both have their text inset a bit from their frames.
If you’re using a textView, for instance, set the textContainerInset to NSZeroSize and see if that works better.

NSTextField's attributed string is delayed in rendering

I've got an Label (NSTextField) in IB that's bound to a controller.
The controller, on awakeFromNIB, sets the attributedStringValue of the label to contain some coloured text and a link or two.
When you see the label it contains the correct string value, but some of the formatting is lost - until you click on the label, and it updates to contain the correct formatting.
I'm using this code to set the value:
[self.testTextField setAllowsEditingTextAttributes:YES];
[self.testTextField setSelectable:YES];
NSMutableAttributedString *linkString = [[NSMutableAttributedString alloc] initWithString:#"hit this "];
[linkString beginEditing];
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:#"link"];
NSRange range = NSMakeRange(0, [attrString length]);
[attrString addAttribute:NSLinkAttributeName value:[[NSURL URLWithString:#"http://google.com"] absoluteString] range:range];
[attrString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSUnderlinePatternDot] range:range];
[attrString addAttribute:NSForegroundColorAttributeName value:[NSColor blackColor] range:range];
[linkString appendAttributedString:attrString];
[linkString appendAttributedString:[[NSAttributedString alloc] initWithString:#" to search"]];
[linkString addAttribute:NSForegroundColorAttributeName value:[NSColor redColor] range:NSMakeRange(0, [linkString length])];
[linkString endEditing];
[self.testTextField setAttributedStringValue:linkString];
Based on this example, you'll see the string coloured red and in the default Label font.
Then when you click on the label the font changes size and face and the link magically renders.
Any ideas on how to get the string to render correctly the first time?
I ran into this same problem. The solution I found was to explicitly set the NSFontAttributeName on the attributed string. I created an NSFont object that matched the font I had set in IB for my textfield and set that attribute like so:
NSFont *font = [NSFont fontWithName:#"Lucida Grande" size:(CGFloat)13.0];
[attrString addAttribute:NSFontAttributeName value:font range:range];
As far as I know that's just normal AppKit weirdness.
I've had success using this custom class to render text fields as links, you just add it in interface builder and set its attributed string value like normal:
DSClickableURLTextField / Swift DSClickableURLTextField fork.
You also have the option of using an NSButton, though that's more of a pain, and you don't get the hand cursor without extra work.

How to draw both stroked and filled text in drawLayer:inContext delegate

this is my drawLayer method in a CALayer's delegate.
it's only responsible for drawing a string with length = 1.
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
CGRect boundingBox = CGContextGetClipBoundingBox(ctx);
NSAttributedString *string = [[NSAttributedString alloc] initWithString:self.letter attributes:[self attrs]];
CGContextSaveGState(ctx);
CGContextSetShadowWithColor(ctx, CGSizeZero, 3.0, CGColorCreateGenericRGB(1.0, 1.0, 0.922, 1.0));
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)string);
CGRect rect = CTLineGetImageBounds(line, ctx);
CGFloat xOffset = CGRectGetMidX(rect);
CGFloat yOffset = CGRectGetMidY(rect);
CGPoint pos = CGPointMake(CGRectGetMidX(boundingBox) - xOffset, CGRectGetMidY(boundingBox)- yOffset);
CGContextSetTextPosition(ctx, pos.x, pos.y);
CTLineDraw(line, ctx);
CGContextRestoreGState(ctx);
}
here's the attributes dictionary:
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSFont fontWithName:#"GillSans-Bold" size:72.0], NSFontAttributeName,
[NSColor blackColor], NSForegroundColorAttributeName,
[NSNumber numberWithFloat:1.0], NSStrokeWidthAttributeName,
[NSColor blackColor], NSStrokeColorAttributeName,
style, NSParagraphStyleAttributeName, nil];
as is, the stroke does not draw, but the fill does.
if i comment out the stroke attributes in the dictionary, the fill draws.
i know this can't be right, but i can't find any reference to this problem.
is this a known issue when drawing text with a delegate ?
as the string is one character, i was following the doc example not using any framesetter machinery, but tried that anyway as a fix attempt without success.
in reading this question's answer, i realized that i needed to be using a negative number for the stroke value. i was thinking of the stroke being applied to the outside of the letter drawn by CTLineDraw, rather then inside the text shape.
i'm answering my own question, in case this should help anyone else with this misunderstanding, as i didn't see the referenced doc covering this.

Resources