NSTextView with shadow - objective-c

I'm trying to add a nice looking shadow to a NSTextViews string, I have this Code so far:
NSShadow *textShadow = [[NSShadow alloc] init];
textShadow.shadowColor = [[NSColor blackColor]
colorWithAlphaComponent:0.3];
textShadow.shadowOffset = NSMakeSize(5.0, -5.0);
textShadow.shadowBlurRadius = 3;
NSDictionary *d = #{NSShadowAttributeName : textShadow,
NSFontAttributeName : [NSFont fontWithName:#"Arial Black" size:36.0],
NSStrokeWidthAttributeName : [NSNumber numberWithFloat:-3.0],
NSStrokeColorAttributeName : [NSColor whiteColor]};
[tv setTypingAttributes:d];
all in all this brings up a pretty looking Drop shadow on the right and the bottom of the string in the NSTextView but because the internal drawing mechanism of the textview seems to draw the "fill" of the Characters first and then the stroke around it, the Shadow lays above the fill of the text in the upper left of the Chars, which looks very bad as you can see here(would post an Image but not enough reputation right now 8-/ )
Is there a better way to add the shadow or a way to "raise" the fill color of the String so it lays above the shadow or is this kind of a Bug in the Foundation framework?
Thanks and greetings,
Alex.

Related

How do I create a horizontal line with text in the middle?

I'm very new to Objective C but I'm looking at the source code of an existing app so there's a lot here to sift through.
I'm trying to create something like this:
-------------- or --------------
...only with a solid horizontal line instead of the dashes.
I have the "or" text defined as this:
_orLabel = ^{
UILabel* label = [[UILabel alloc] init];
label.translatesAutoresizingMaskIntoConstraints = NO;
label.textColor = [UIColor blackColor];
label.font = [UIFont mainFontWithSize:[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline].pointSize];
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = NSTextAlignmentCenter;
label.text = NSLocalizedString(#"or", nil);
[view addSubview:label];
return label;
}();
That's working great to get "or" to show up but I have no idea how to get the horizontal lines on either side.
I would subclass UILabel and override drawTextInRect:. The easiest way is to call super so that the text gets drawn. Now you are still in a graphics context (CGContext) so you can use ordinary Quartz drawing commands to draw your horizontal lines.
The simplest approach is to make use of certain Unicode characters that will give you a solid line.
label.text = #"───────── or ─────────";
That text is using a series of "BOX DRAWINGS LIGHT HORIZONTAL" characters (U+2500).
I would use IB to add a view on each side of the label. Make the views have a height of 1 or 2 with a black background, and use autolayout to make them size appropriately for the label and screen situation:
Make view #1 anchored to the left edge of the screen and the right side of the label.
Make view #2 anchored to the right edge of the screen and the left side of the label.
Make the 2 views and the label have the same vertical center.

Mac OSX: How to stroke text in NSTextField?

I've programming an application for the OSX using Objective C. I have an NSTextField that I'm using to display uneditable text. I'm trying to make the font have a thin stroke/outline around it but struggling to do so.
I've tried to use NSTextView instead of NSTextField and implemented an NSAttributedString as so, however the text is not being outlined at all:
NSAttributedString *string = [[NSAttributedString alloc] initWithString:#"Test String" attributes:#{ NSStrokeColorAttributeName : [CIColor blackColor], NSForegroundColorAttributeName : [CIColor blackColor], NSStrokeWidthAttributeName : #-1.0 }];
[quoteText_Label setEditable:YES];
[quoteText_Label insertText: string];
[quoteText_Label setEditable:NO];
Looking at the other question of SO, they're either aimed at iOS, Swift, or are an overkill for the simple implementation I'm attempting to go for: just a simple black stroke around white font.
Thank you
Switch on Rich Text in the XIB and set the attributedStringValue property of the text field. You don't have to do setEditable.
Read the documentation of NSStrokeWidthAttributeName:
NSNumber containing floating point value, as percent of font point size
Default 0, no stroke; positive, stroke alone; negative, stroke and fill (a typical value for outlined text would be 3.0)
A thin black outline around black text is hardly visible.
Values of the color attributes should be NSColor objects. I think, using CIColor is wrong here.

Mavericks Style Tagging

I'm quite new to cocoa and I'm trying to find out how I can create something similar to the new tagging UI in Mavericks:
I assume, I'll have to overwrite NSTokenFieldCell to get the coloured dots or an icon on the tags. But how does this popup list work?
Thanks for your help!
Sadly, you'll have to roll your own. Almost all of the drawing taking place in NSTokenFieldCell is private, so adding any kind of ornamental elements would have to be done by you. If I remember correctly, NSTokenFieldCell uses an NSTokenTextView instead of the window's standard field editor. I'm not sure what's different about it, but I think it's mostly to deal with the specialized nature of "tokenizing" attributed strings. I think they just use NSAttachmentCell objects for the graphical tokens, and when the cell receives a -mouseDown: event, they show the menu.
The menu part would actually be pretty easy because you can add images to menu items like so:
NSMenuItem *redItem = [[NSMenuItem alloc] initWithTitle:#"Red"
action:#selector(chooseColorMenuItem:)
keyEquivalent:#""];
// You could add an image from your app's Resources folder:
NSImage *redSwatchImage = [NSImage imageNamed:#"red-menu-item-swatch"];
// ----- or -----
// You could dynamically draw a color swatch and use that as its image:
NSImage *redSwatchImage = [NSImage imageWithSize:NSMakeSize(16.0, 16.0)
flipped:NO
drawingHandler:^BOOL(NSRect dstRect) {
NSRect pathRect = NSInsetRect(dstRect, 0.5, 0.5); // Aligns border to integral values
NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:pathRect];
NSColor *fillColor = [NSColor redColor];
NSColor *strokeColor = [fillColor shadowWithLevel:0.5];
[fillColor setFill];
[path fill];
[strokeColor setStroke];
[path stroke];
return YES;
}];
redItem.image = redImage;
With respect to the token drawing stuff, take my info with a grain of salt because Apple's documentation on this stuff is pretty lacking, so everything I'm telling you is from personal struggles, cursing, and head-banging. Anyway, I'm sorry I couldn't bring you better news, but I guess, it is what it is. Good luck.

Why doesn't this code draw white text?

This code doesn't draw white text, why?
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment:NSCenterTextAlignment];
NSFont *font = [NSFont fontWithName:#"System" size:13];
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:style, NSParagraphStyleAttributeName, font, NSFontAttributeName, [NSColor whiteColor], NSForegroundColorAttributeName, nil];
[button.title drawInRect:textRect withAttributes:attrs];
(Assuming the [cocoa] tag doesn't mean cocoa touch)
It's because NSButton is likely overriding the choice you've made to draw it's text in when it's -drawRect: gets called again. You can apply the attributes you've given in that dictionary to an NSAttributedString and call setAttributedTitle: to keep your style choices around.
If you need more fine-grain control over text rendering, either edit and move your logic into -drawRect: if it isn't already there, or provide an NSTextField or NSTextView as appropriate.
The main problem with the code you've provided is that #"System" isn't a font name.

NSParagraphStyle line spacing ignored

A simple test that is failed: Make a new project with just one subview (UITextView) and put the following in:
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineHeightMultiple = 50.f;
paragraphStyle.lineSpacing = 100.f;
paragraphStyle.minimumLineHeight = 200.f;
paragraphStyle.maximumLineHeight = 500.f;
UIFont *font = [UIFont fontWithName:#"AmericanTypewriter" size:24.f];
self.textView.attributedText = [[NSAttributedString alloc] initWithString:
#"This is a test.\n Will I pass?" attributes:
#{NSParagraphStyleAttributeName : paragraphStyle, NSFontAttributeName : font}];
}
Line spacing is the same as if the attribute were not there. Has anything got this to work successfully? I put in ridiculous numbers just to show that it won't change...
This is a bug in NSHTMLWriter which is the private class which UITextView uses to convert attributedText into HTML. Internally it displays this HTML via a UIWebDocumentView. Read more on the inner workings of UITextView in my writeup here: http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/
The problem comes from an easy to miss speciality in the font CSS shorthand. If you specify a pixel size with the font shorthand then this sets BOTH the font-size as well as the line-height. Since NSHTMLWriter puts the font AFTER the line-height this causes the line-height to be cancelled out by the font size.
See here for my Radar which includes the full analysis of the bug: http://www.cocoanetics.com/2012/12/radar-uitextview-ignores-minimummaximum-line-height-in-attributed-string/
I suggest you file a bug report as well and mention my Radar #12863734.
I don't know if this is enough for your purposes but I could adjust the line spacing by setting the minimum and maximum line height. Furthermore to use a font I put it into the font property of the text view rather than passing it as the value of NSFontAttributeName in the attributes dictionary. (Maybe this part is not (well) documented?)
About your attributes
lineSpacing is calculated from the bottom of the line to the bottom of the upper line and that space is constrained to values between minimumLineHeight and miximumLineHeight. What I am trying to say is that maybe some values in your attributes are cancelling or overriding others.
Also if you need to just adjust the spacing between line you probably don't need to use paragraphStyle.lineHeightMultiple :)
The code
This worked for me:
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.minimumLineHeight = 35.f;
paragraphStyle.maximumLineHeight = 35.f;
UIFont *font = [UIFont fontWithName:#"AmericanTypewriter" size:18.f];
NSString *string = #"This is a test.\nWill I pass?\n日本語のもじもあるEnglish\nEnglish y Español";
NSDictionary *attributtes = #{
NSParagraphStyleAttributeName : paragraphStyle,
};
self.textView.font = font;
self.textView.attributedText = [[NSAttributedString alloc] initWithString:string
attributes:attributtes];
Additional Notes
There seems to be a situation with Japanese/Chinesse and maybe other characters mixed with alphabet characters in the same line. It will make that line to have a bigger leading to solve that you need to set up the minimum and maximum line height as I did.
You can see the problem when rendering my example without attributes.
Setting maximumLineHeight seems to resolve this issue for me;
CGFloat fontSize = 22.f;
titleLabel.font = [UIFont boldSystemFontOfSize:fontSize];
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
paragraphStyle.maximumLineHeight = fontSize/2;
titleLabel.attributedText = [[[NSAttributedString alloc]
initWithString:#"This is a test.\nWill I pass?"
attributes: #{ NSParagraphStyleAttributeName : paragraphStyle,
NSFontAttributeName : titleLabel.font}]
autorelease];
For this particular string you need to set paragraphSpacing instead. What's about lineSpacing, I believe it's just not supported yet on iOS.
As nacho4d answered, in iOS 6 you need to use minimumLineHeight and maximumLineHeight and set font directly in UITextView, not in NSAttributedString as line height in that case will be overridden.
Please note that when you set font in UITextView, the "editable" property of UITextView should be set to YES, in other case attributed text would not be affected.
These issues are present only in iOS 6. In iOS 7 and above everything is ok;
In my case, none of the paragraph styling was working. The fix was to set the attributed text on the label AFTER doing any frame adjustments on the label. :)