Showing posts with label cocoa. Show all posts
Showing posts with label cocoa. Show all posts

20100530

NSMenu popup location

NSMenu has a class method popUpContextMenu:withEvent:forView:, which I initially thought was the only way to cause a menu to pop-up where I wanted. In order to give it a useful event, I took the event I had and created a new one from it, with a different mouse location, to try to trick the menu's position. The goal is that the menu should normally appear under the control, with the left of the menu aligned to the left of the control. If there isn't enough room under the control, the menu should appear above the control and not obscure it from view. Similarly, if there isn't enough space to the right of the control for the entire menu to display, the menu should right-align to the control.
I had gotten the above/below positioning sorted out using the NSMenuDelegate method confinementRectForMenu:onScreen:, but I could not get the left/right alignment switch to work. If I would set the menu rect's origin to the left of the control, the upper-right of the menu would be there; and if I set it to the right of the control, the menu's upper-left would be there.
I put the menu to the left of the control entirely and then moved the x field of the origin over by the width of the menu. No good.
I tried moving it over incrementally over several test runs. It eventually became clear that I could not move the x coordinate of the menu's origin past 5/6 of the width of the control without the menu aligning its left to the left of the control.

I then discovered that NSMenu has an instance method popUpMenuPositioningItem:atLocation:inView:. This method had much better documentation, saying that I could pass nil for the item and the view, and then I could control the upper-left corner of the menu rect, and it would pop-up unattached to any other window. I tried this out, and it worked, with less code (no longer needed to fake an NSEvent).
I went back and used a nil view for the class method, and this also worked.

My recommendation is to use the class method, since it provides the flexibility to position an arbitrary item in the menu or the entire menu at a certain point, and it has much better documentation.

20100127

NSThread

The project I'm working on uses threads. Like a good Cocoa app, the main thread is in charge of the GUI, and the worker threads run in the background so that we don't pinwheel.
In order to know when the thread has finished its work, we registered for notification:

[[NSNotificationCenter defaultCenter] addObserver:self
selector:aSel
name:NSThreadWillExitNotification
object:thread];


and in the aSel method, use ivars to update an NSTableView's dataSource.

We had a really pesky race condition that, after two days, we realized was caused by aSel running in the same worker thread! It turns out that this is in the documentation, that notifications are served to the same thread that posts them. However, this is a fairly useless implementation decision.

Our workaround is not to register for the notification, but rather to have the thread performSelectorOnMainThread:withObject:waitUntilDone: at the end of its execution. There is still a problem that we'd like to release the thread at that point, but we don't know what will happen if we try that!

20090527

awakeFromNib

What does awakeFromNib actually mean? Using the Cocoa principle of "it's probably what it sounds like," a naive programmer would assume that this method is invoked when the program awakes, i.e. receives focus. Alas, this is far from accurate. Someone seems to think that an object created via nib file instead of programmatically requires extra code, and that's possibly true; but "awake from nib?" Where's the waking up?

A nib file is an xml archive that in a sense dehydrates Cocoa objects for runtime reconstitution. However, it can require further tweakage, so every thus-created object receives the awakeFromNib message. It can be useful, for instance, in establishing connections; the app delegate manager receives awakeFromNib before applicationDidFinishLaunching, so I use it to create some connections such as (instance variable) dockTile = [NSApp dockTile]; which I can then refer to everywhere else in my class, knowing that it will have a valid value immediately after the object is instantiated, before any other code can run.


- (void)awakeFromNib
{
  dockTile = [NSApp dockTile];
}

20090518

Cocoa app delegate

Programming with the Cocoa APIs has been a very strange experience for me. My first experience was on the iPhone with Cocoa Touch, and right when I had sort-of figured that out, I had to switch to OS X development.
Yesterday, I figured out how to badge dock icons. I found the appropriate documentation, but I still didn't understand, so I downloaded the sample project. Now I had proof of existance, but couldn't see how it was working. Finally, I opened the nib, and saw that the AppDelegate class was connected via Interface Builder as the NSApplication's delegate, and thus its delegate functions were called as appropriate.
I cannot figure out how to achieve this same goal programmatically, so I copied their method, and it does indeed work.

EDIT: the following is untested, but seems like it should work:
Create an instance of the delegate class, and in its -awakeFromNib method, insert the follwing code:
[NSApp setDelegate:self];

the end.