Selectable area for NSButton - cocoa

So I am basically trying to make a list of selectable text items (just a list of text, no button bezels, backgrounds, etc.). I suppose that I could make this happen with an NSTableview, but trying to make the table view completely transparent and still functional was giving me some issues. Anwyays, I am trying to do it with NSButtons that I create programatically and add to my view in a list, without any background or bezel. However, when I set the properties to make the button transparent and without bezel, the clickable area of the button is relegated to the text of the title of the button alone. Clicking anywhere else that the button should be (around the title) no longer works. Here is the code I am using. I want to be able to click anywhere in the rect in which I create the button in order to cause a click. FYI I have tried NSSwitchButton without the checkbox image and it is the same thing. Thanks for your help!
for(NSString *theTask in theTasks){
NSButton *theCheckBox = [[[NSButton alloc] initWithFrame:NSMakeRect(xCoordinate + 25, yCoordinate + ([tasksWindow frame].size.height/2) - 60, [tasksWindow frame].size.width - 40, 25)] autorelease];
[theCheckBox setButtonType:NSToggleButton];
[theCheckBox setAction:#selector(taskChecked:)];
[[theCheckBox cell] setBackgroundColor:[NSColor clearColor]];
[[theCheckBox cell] setBordered:NO];
NSAttributedString *theTitle = [[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%#", theTask] attributes:[NSDictionary dictionaryWithObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName]] autorelease];
[theCheckBox setAttributedTitle:theTitle];
[[tasksWindow contentView] addSubview:theCheckBox];
yCoordinate -= 20;
}
UPDATE: I've been able to confirm that setting the background color to clear is what seems to cause the button to stop responding to clicks within its full boundaries (not the removal of the border).

So to answer my own question, it was because I was overlaying the transparent buttons atop a transparent NSWindow (which refuses mouse events). I simply had to set the window NOT to ignore mouse events and the behavior went away.

Related

selectable NSTextField and NSColorPanel – how to break their undesired interplay?

In a seemingly trivial setup, I encounter an undesired interplay between a selectable NSTextField and an NSColorPanel that I cannot get rid of and that drives me nuts.
Here’s the setup: Within one window, I have a selectable Multi-Line Label (de facto an NSTextField) and an NSColorWell.
The Color Well allows the user to color geometric objects in the GUI; it has nothing to do with text whatsoever. Of course, clicking on the color well activates it, i.e. brings up the shared NSColorPanel and connects the color well to it.
The Text Field is completely independent from the colored objects in the GUI and presents data to the user. It is read-only, i.e. not editable. Since the data is organized in columns, I use tabs for text formatting and the setAttributedStringValue: method of NSTextField to display the data.
At first glimpse, everything works as you would expect in a such a trivial setup.
But here comes the rub: I want the user to be able to copy the data in the text field to process it elsewhere. Therefore, the NSTextField has to be selectable. And setting it to be selectable is where the problems start:
When the user clicks on the selectable text field to select the text, the window’s field editor takes over, and as a consequence, all the tab settings of the attributed text are lost and the text gets mingled. The usual way to prevent this is to set the allowsEditingTextAttributes property of the NSTextField to YES. If I do this, the tab formatting is preserved when the user selects the text. But now the NSColorPanel (if visible) unintentionally also switches to the text color (always black), and if the color well is active (connected to the NSColorPanel), it will remain active, thereby changing the color of all geometric GUI objects to black. Ouch!
I have found no way to set the selectable and allowsEditingTextAttributes properties of NSTextField to YES but still prevent it from communicating with the NSColorPanel.
The obvious alternative route would be to preserve the tab formatting for selected text even with allowsEditingTextAttributes set to NO (which would disconnect the color panel from the text field, as desired). But I’ve had no success with this approach either, although I do not really understand why:
My idea was to set the required tabs as the defaultParagraphStyle of the field editor of the text field. So, I set up a customized field editor:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSArray *myTabs = #[
[[NSTextTab alloc] initWithType:NSRightTabStopType location:100],
[[NSTextTab alloc] initWithType:NSRightTabStopType location:200],
[[NSTextTab alloc] initWithType:NSRightTabStopType location:300]
];
NSMutableParagraphStyle *myParagraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
[myParagraphStyle setTabStops:myTabs];
myFieldEditor = [NSTextView new]; // myFieldEditor is an instance variable
[myFieldEditor setDefaultParagraphStyle:myParagraphStyle];
[window setDelegate:self];
[window fieldEditor:YES forObject:myTextField];
}
And activate it for the text field in the windowWillReturnFieldEditor:toObject: delegate method:
- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
{
if (client == myTextField) return myFieldEditor;
return nil;
}
I even made sure that my custom field editor is indeed used by subclassing the NSTextFieldCell of my text field and logging the propagated field editor:
#implementation myTextFieldCell
- (NSText *)setUpFieldEditorAttributes:(NSText *)textObj
{
NSTextView *newTextObj = (NSTextView*)[super setUpFieldEditorAttributes:textObj];
NSLog(#"STYLE: %#", [newTextObj defaultParagraphStyle]);
return newTextObj;
}
#end
Now, when I select the text in the text field, I get the following log output:
2017-11-02 11:51:07.432 Demo[94807:303] STYLE: Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
100R,
200R,
300R
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningFactor 0.05, HeaderLevel 0
Which is exactly what is expected.
But still, the tab formatting disappears in the text field as soon as I select the text. I have no idea why this does not work.
So I’m stuck either way. If I set the allowsEditingTextAttributes property of NSTextField to YES, tab formatting is preserved when the text is selected, but my colored objects in the GUI unintentionally change to black. If I set the allowsEditingTextAttributes property to NO, the color panel behaves as it should, but the tab formatting is lost as soon as I select the text.
This is a very unfortunate case of Cocoa trying to be too smart and thereby making a completely trivial setup a huge issue.
Any ideas anyone?
OK, so I ended up with the suggestion that #Willeke (thanks!!) made in his comment to my question: To use NSTextView instead of NSTextField to implement my Multi-Line Label.
I will first sum up why it seems impossible to do what I wanted with NSTextField, and then the solution with NSTextView.
Why NSTextField doesn’t work
As described, my idea for a solution was to customize the field editor for the NSTextField, setting the tab stops I need, so that I need not set NSTextField’s allowsEditingTextAttributes property to YES (which would unintentionally couple the text field to the color panel). This, I hoped, would preserve the tab stops of my attributed string’s paragraph style when I select the text in the text field and thereby activate the field editor.
Extensive testing has shown that this does not work for several reasons:
As #Willeke pointed out, setting the usesFontPanel property of NSTextView to NO also breaks the connection of the text view to the color panel (as desired). However, this does not work for the NSTextView that is the field editor of the NSTextField, because in this context, this setting is always overwritten by the allowsEditingTextAttributes property of the NSTextField: If allowsEditingTextAttributes is YES, the font and color panels are coupled regardless of the value of usesFontPanel, if it is NO, the font and color panels are decoupled regardless of the value of usesFontPanel.
The idea to use the tab stops of a customized field editor instead of using the tab stops of my attributed string’s paragraph style (which would require allowsEditingTextAttributes to be YES) wouldn’t work, anyway, because the tab stops settings of the field editor are obviously always completely ignored by NSTextField, regardless of the value of the allowsEditingTextAttributes property. NSTextField always uses the evenly spaced default tab stops.
Judging from intense googling, the other variant – setting allowsEditingTextAttributes to YES but somehow modifying NSColorPanel to not connect to the NSTextField nevertheless – is impossible to implement without recurring to private methods of NSColorPanel.
How to implement the solution with NSTextView
While it seems overkill to instantiate a complete NSTextView embedded in a clip view and a scroll view just to get the functionality of a text field, in the end it’s the easiest (or even only possible) solution.
To make the scroll view disappear, you’ll have to basically uncheck everything in the NSScrollView’s Attribute inspector in IB, in particular Show Vertical Scroller. Set Draw Background and the border type to the kind of appearance you want; if you want to mimic a multi-line label (like I did), uncheck Draw Background and choose the invisible border type. In the Attribute inspector of the embedded NSTextView, also uncheck all attributes except Selectable, in particular Uses Font Panel.
Make sure the size of the NSTextView is large enough to hold the complete content string to avoid unintentional scrolling effects and fix the text position. If your content string ends with a line brake, you’ll need enough space for an empty line beneath it. If you did not uncheck Draw Background and this does not look the way you want, don’t draw the background of either the NSScrollView or the NSTextView, select the invisible border for NSScrollView and then put an NSBox of the desired size and appearance beneath them.
You can now set the attributed content string with:
[[myTextView textStorage] setAttributedString:myAttributedString];
Note that this does work although the editable property of NSTextView is set to NO since you’re modifying the NSTextStorage, not the NSTextView itself.
But unfortunately, we’re not finished yet.
When you’re using an NSTextField to display readonly data as you typically do in a Label, more often than not you would not want the text field to be part of your key view loop (that circles through your controls by pressing the Tab key). To achieve this, you can simply set the refusesFirstResponder property of NSTextField to YES. But NSTextView does not inherit from NSControl and therefore does not have this property. So in the end, we’ll have to subclass NSTextView to add the refusesFirstResponder property.
The implementation overwrites becomeFirstResponder and goes like this:
- (BOOL)becomeFirstResponder
{
if (!_refusesFirstResponder) return [super becomeFirstResponder];
NSEvent *event = [NSApp currentEvent];
if ([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown) return [super becomeFirstResponder];
NSView *validKeyView = ([event modifierFlags] & NSShiftKeyMask)? [[[self previousValidKeyView] previousValidKeyView] previousValidKeyView] : [self nextValidKeyView];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{[[self window] makeFirstResponder:validKeyView];}];
return NO;
}
If refusesFirstResponder is NO, we simply return super’s implementation.
If it is YES, we check if the NSTextView is about to become first responder because of a mouse click within it. If so, we also simply return super’s implementation, thereby allowing text selection with the mouse.
Other than that, we forward the first responder request to the next or previous key view (depending on whether the Shift key was pressed) and return NO, refusing to become first responder. Determining the previous key view is a bit tricky because the closest previous key view is the embedding NSClipView we don’t want or need, but have to use because Interface Builder does not offer a “pure” NSTextView. Then comes the embedding NSScrollView, and only then the previous key view we actually want.
Also, since we’re amidst a process which determines the first responder, we cannot simply invoke makeFirstResponder:, but have to postpone it to the next iteration of the run loop.
Now that we have implemented refusesFirstResponder, we’ll still have to mimic NSTextField’s behavior to dismiss any text selection when it loses first responder state. We can do this in an NSText delegate method. Assuming we don’t need other delegate functionality, we can make our subclass its own delegate and add this delegate method:
- (void)textDidEndEditing:(NSNotification*)notification
{
[[notification object] setSelectedRange:NSMakeRange(UINT64_MAX, 0)];
}
Finally, if we have to subclass, anyway, we might as well add a setAttributedString: convenience method.
So what we’ll end up with is this:
Header:
#import <Cocoa/Cocoa.h>
IB_DESIGNABLE
#interface MyTextFieldLikeTextView : NSTextView <NSTextViewDelegate>
#property IBInspectable BOOL refusesFirstResponder;
- (void)setAttributedString:(NSAttributedString*)attributedString;
#end
Implementation:
#import "MyTextFieldLikeTextView.h"
#implementation MyTextFieldLikeTextView
- (void)awakeFromNib
{
[self setDelegate:self];
}
- (BOOL)becomeFirstResponder
{
if (!_refusesFirstResponder) return [super becomeFirstResponder];
NSEvent *event = [NSApp currentEvent];
if ([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown) return [super becomeFirstResponder];
NSView *validKeyView = ([event modifierFlags] & NSShiftKeyMask)? [[[self previousValidKeyView] previousValidKeyView] previousValidKeyView] : [self nextValidKeyView];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{[[self window] makeFirstResponder:validKeyView];}];
return NO;
}
- (void)textDidEndEditing:(NSNotification*)notification
{
[[notification object] setSelectedRange:NSMakeRange(UINT64_MAX, 0)];
}
- (void)setAttributedString:(NSAttributedString*)attributedString
{
[[self textStorage] setAttributedString:attributedString];
}
#end
Still a lot of effort only because Cocoa tries to outsmart us and insists on connecting the NSColorPanel to each and every NSTextField that allows for attributed text …

Trouble matching the vibrant background of a Yosemite NSMenuItem containing a custom view

I am attempting to add a custom view to an NSMenuItem in the OS X 10.10 Yosemite menu bar.
The custom view is simply an NSView background with an NSTextField “label”.
The problem is that the background NSView is given Yosemite-style vibrancy/transparency when added to the menu. The NSTextfield label is not.
Through the use of NSRectFillUsingOperation I've gotten this to look good for some background colors in Yosemite. But others continue to not match. When it is working, after manually "highlighting" the view, the original colors change and no longer match. I can dig up some example code for this if needed.
Then, when it is looking somewhat good in Yosemite, it looks terrible in 10.9 Mavericks.
I've also tried setting the wantsLayer property to YES to turn the view into a CALayer-backed view. This creates other issues such as text not anti-aliasing correctly against a clear background.
My Question:
How do I display a label on top of a NSMenuItem custom view? The label's background must exactly match the view's background. Solution must work in Yosemite and Mavericks.
Example code below:
self.statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength];
[self.statusItem setTitle:#"TEST"];
[self.statusItem setHighlightMode:YES];
[self.statusItem setEnabled:YES];
[self.statusItem setTarget:self];
NSMenu *menu = [[NSMenu alloc] init];
[menu addItemWithTitle:#"Disabled menu item" action:nil keyEquivalent:#""];
[menu addItemWithTitle:#"Enabled menu item" action:#selector(enabled) keyEquivalent:#""];
NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(30, 20, 50, 20)];
label.stringValue = #"label";
label.editable = NO;
label.bordered = NO;
label.backgroundColor = [NSColor blueColor];
//label.backgroundColor = [NSColor clearColor];
PKMenuItemView *view = [[PKMenuItemView alloc] initWithFrame:NSMakeRect(0, 0, 200, 50)];
[view addSubview:label];
NSMenuItem *viewMenuItem = [[NSMenuItem alloc] init];
[viewMenuItem setView:view];
[menu addItem:viewMenuItem];
self.statusItem.menu = menu;
I've subclassed the NSView to override drawRect: and draw a colored background:
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
[[NSColor blueColor] setFill];
NSRectFill(dirtyRect);
//NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
}
It is surely kinda hack, but it worked for me.
Try adding an NSImageView with empty image to your custom view. Image view must be occupy the whole view.
I think I have less "hackish" solution. It's indeed caused by the new NSVisualEffectView and Vibrancy stuff in Yosemite. I learned that there are quite complex rules how views are drawn when they're subviews of NSVisualEffectView. It was discussed on WWDC 2014 in session 220 - Adopting Advanced Features of the New UI of OS X Yosemite. I recommend you to watch this session video to get comprehensive explanation.
Shortly, it seems that your problem may be caused by colors you use. There are two new system colors - [NSColor labelColor] and [NSColor secondaryLabelColor]. These two are automatically adjusted when drawn inside NSVisualEffectView. Also, your custom view should support Vibrancy effect. This is done by overriding - (BOOL)allowsVibrancy method and returning YES.
Please check the session video mentioned above or download session slides in PDF to get precise information. This stuff is discussed from slide 124 in PDF and near the middle of the video.
Unfortunately there are currently several problems in Yosemite. As Matthes already mentioned, you can use labelColor() and secondaryLabelColor(). Using those colors do not cause the label to draw the strange background you are seeing.
However, labelColor() only works fine for VibrantDark because there the label color is white when a NSMenuItem is both highlighted and when not highlighted. With VibrantLight the labelColor is black and is therefore very difficult to read on on top of the blue highlight.
For the highlight color of the custom view of your NSMenuItem one might think that you should use selectedMenuItemColor() given its name. The problem with this is that the color doesn't actually match the menu highlight color that you see in NSMenuItems without a custom view. The color is completely wrong for both VibrantLight and VibrantDark.
Tl;dr: So how can you create a custom NSMenuItem that uses the exact same text color and highlight color? You can't. You should use labelColor() and selectedMenuItemColor() but the former only works correctly for VibrantDark, and the latter doesn't match at all.
I really hope I am wrong because I am trying to achieve the same thing :(
Edit: Here is an example project if people want to have a look.
Response from a Apple Developer Technical Support ticket I opened in 2015:
Re: DTS Auto-Ack - Vibrant background and highlighting of Custom View NSMenuItems
This is a difficult problem to tackle, especially in light of the fact that menu selection drawing was not intended for menu items with custom views, and menu selection drawing (colors, etc.) may change in the future. This is why we ask you to file bug reports so that menu selection will be honored with custom views, if asked for, so that future changes to OS X won’t require developers to continually maintain their code to match future color appearances.
The “Application Menu and Pop-up List Programming Topics” says this:
Views in Menu Items -
“A menu item with a view does not draw its title, state, font, or other standard drawing attributes, and assigns drawing responsibility entirely to the view. Keyboard equivalents and type-select continue to use the key equivalent and title as normal.”
Since all drawing is up to the developer, custom views in menu items aren’t necessarily supposed to draw “selected”.
The APIs to obtain the right selection color is obviously not doing what it’s supposed to, hence the request to file a bug report. I wish we could offer more concrete solutions to the problem but a workaround offered today may not hold up tomorrow and we don’t want to set a bad precedent on workarounds that are risky. Apple apps have access to lower level private APIs that achieve their results. We cannot offer you these solutions as they are private.
If selectedMenuItemColor() does not match the menu highlight color with Vibrant light and dark, that’s a bug to be filed and to be fixed.
Lastly, Apple recommends to use NSMenuItem’s APIs as much as possible to achieve what you want in menus. The screenshots you included can likely be done without applying custom views.
I've just discovered that +[NSColor keyboardFocusIndicatorColor] is the right color (on El Capitan at least), whereas the expected selectedMenuItemColor is by far too dark.
Per AppKit engineers at WWDC, this doesn't really work with NSMenuItem. I added that answer to this question as well.
They suggested to instead use an NSPopover to create a faux-NSMenu attached to an NSStatusItem menu bar helper.
Using code similar to the below results in vibrant background selection:
override func viewDidLoad() {
super.viewDidLoad()
let visualEffectView = NSVisualEffectView()
visualEffectView.material = .selection
// .menu or .popover for the non-selected background.
visualEffectView.state = .active
visualEffectView.blendingMode = .behindWindow
visualEffectView.isEmphasized = true
let label = NSTextField(labelWithString: "Hello, world!")
label.cell?.backgroundStyle = .emphasized
visualEffectView.addSubview(label)
visualEffectView.frame = view.bounds
label.setFrameOrigin(.zero)
view.addSubview(visualEffectView)
}
At the WWDC 2019 AppKit Lab I worked through this issue with engineers from the AppKit team.
They were surprised that it did not work by default, and encouraged me to file (more) radars:
FB6143574 - Expose private API for NSMenuItem _viewHandlesEvents
They were aware of a private API _viewHandlesEvents on NSMenuItem.
// VibrantMenuBar-Bridging-Header.h
#import <AppKit/AppKit.h>
#interface NSMenuItem ()
#property (setter=_setViewHandlesEvents:) BOOL _viewHandlesEvents;
#end
Set viewHandlesEvents to false and the background of the custom view in the NSMenuItem will be selected and appear (somewhat) as expected.
There are still issues with how labels and other subviews react to the selection. Text View text is not properly changing color.
let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "")
menuItem.view = label
menuItem._viewHandlesEvents = false
There are some other references to _viewHandlesEvents on the internet:
How to flash a custom NSMenuItem view after selection?

Dragging an NSTextField inside of a window

Im attempting to make a little app that lets you add text boxes to you canvas (window). I have an NSTextField that needs to let you drag it around the window. When you drop it it needs to stay in the spot you mouse left it. Heres my code to make the fist text field:
NSTextField *myTextField=[[NSTextField alloc] initWithFrame:NSMakeRect(200.0, 200.0, 200.0, 25.0)];
[myTextField setBordered:NO];
[myTextField setStringValue:#"Double Click to edit"];
[[window contentView] addSubview:myTextField];
//Some sort of dragging code for myTextField
If anyone has ever done something like this any help is really appreciated.
My first instinct would be to create a subclass of NSTextField and override some or all of mouseDown:, mouseDragged:, and mouseUp: to create the dragging behavior you want. The mouse events section in Apple's Event-Handling Guide might be helpful.

NSTextView not refreshed properly on scrolling

I have a NSTextView with a sizeable quantity of text. Whenever I scroll however, the view isn't updated properly. There are some artifacts that remain at the top or the bottom of the view. It appears that the view doesn't refresh itself often enough. If I scroll very slowly the view updates correctly though. If I add a border to the view everything works perfectly, borderless view is the one that has a problem. Here's a link to a screenshot:
Thanks
Have you set the setDrawsBackground and copiesOnScroll propertes for either the NSScrollView or the NSClipView?
The first thing I would suggest is turning off the "draws background" property of the NSScrollView:
[myScrollView setDrawsBackground:NO];
Note that this should be set on the NSScrollView, and not on the embedded NSClipView.
The following excerpt from the documentation may be relevant:
If your NSScrollView encloses an NSClipView sending a setDrawsBackground: message with a parameter of NO to the NSScrollView has the added effect of sending the NSClipView a setCopiesOnScroll: message with a parameter of NO. The side effect of sending the setDrawsBackground: message directly to the NSClipView instead would be the appearance of “trails” (vestiges of previous drawing) in the document view as it is scrolled.
Looks like the text field isn't even in the scrolling-area... Are you sure something isnt overlapping it?
I had a similar trouble - artifacts develop when the NSTextView is embedded in another scrollview (ie. a NSTableView).
I actually turned on the setdrawsbackground, and then added a nice color to make it disappear again.
-(void)awakeFromNib{
NSScrollView *scroll = [self enclosingScrollView];
[scroll setBorderType:NSNoBorder];
[scroll setDrawsBackground:YES];
[scroll setBackgroundColor:[NSColor windowBackgroundColor]];
}
This in combination with a scrollWheel event let me use the NSTextView in a NSTableView.
-(void)scrollWheel:(NSEvent *)theEvent{
NSScrollView *scroll = [self enclosingScrollView];
[[scroll superview] scrollWheel:theEvent];
}
I had the same trouble some time ago. I don't remember how I solved it.
Try to place the NSTextView to another view if the superview is a custom view. Just to see what will happen.

Cocoa: [statusItem setView:myView] makes a white bar menu item no matter what

In my small app for Mac OS X I display some info in system menubar. I use
statusItem = [
[[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength]
retain
];
It works very nice and I can change the text with
[statusItem setTitle:[NSString stringWithString:#"Woo-hoo"]];
But it uses the default menu font which is too big for my relatively unimportant info. So I decided to reimplement it with a custom view. I created a view in Interface Builder.
Unfortunately, however, when I set it as a view for my menu item with
[statusItem setView:myView];
it just displays a white bar in the menu instead of my thing. I tried to
[statusItem
drawStatusBarBackgroundInRect:[myView frame]
withHighlight:NO];
with no success.
In trying to figure out whether a problem is with the view itself or with the way I assign it to the menubar, I created a window and did
[myTestWindow setContentView:myView];
This one worked seamlessly. This makes me think my view is OK :-)
So, what else can I try to make the menu item display my own view?
Thanks!
It happened to be some weird side-effects of window-view autosizing setup in Interface Builder (let’s call them size-effects). In the Inspector you can setup how subviews get resized upon superview sizing. And so it was somehow broken in my case, such that when window gets small enough (menuitem-high), my elements just got drawn outside of the window’s frame.
I re-configured the sizing in IB, eliminating all the automatics I don’t need, and now it works perfectly: the view from IB gets displayed inside a menu item.
What is the height of the frame of the view? Maybe your view is taller than the menubar and you are drawing outside of it. The current menubar is 22 pixels, but you should ask the systemStatusBar for it's thickness, just in case it ever changes.
Try drawing a frame around your view to see if you are getting anything.
[[NSColor blueColor] set];
NSBezierPath *path = [NSBezierPath bezierPathWithRect:self.bounds];
[path setLineWidth:4.0f];
[path stroke];
If you get just an 'L' shape (the bottom left corner) of blue then the view is too large. If you get a rectangle but still no text then you may not be drawing the text inside the view, look at the coordinates you are drawing the text at (and review View Geometry). Putting the view in a window may have worked because it is larger.
For an example of using text in a status menu view take a look at Matt Gemmell's NSStatusItemTest project.
EDIT:
Sorry, somehow I missed where you said you created the view in IB. I did a quick test and I can see the white box you mentioned.
The docs for NSStatusItem's setView: states
The custom view is responsible for
drawing itself and providing its own
behaviors, such as processing mouse
clicks and sending action messages.
And status item views go into a special (apple private) window called NSStatusBarWindow that may have different internal behavior than normal windows and certainly seems to not support views from IB.
So yes, I think you need to create a custom NSView subclass and do your own drawing in drawrect:.

Resources