OSX application without #NSApplicationMain - xcode

So here i was trying to get rid of #NSApplicationMain to get familiar on how things work more in depth.
I found this nice answer from musically_ut here OSX application without storyboard or xib files using Swift
However, its not quite working !
Here's the weird result : the view is not linked to the window, furthermore, the window is not where it should be!
the image can be found here http://imgur.com/e4EXnpW
I started the xcode project without using storyboard, then created main.swift, AppDelegate.swift, ViewController.swift, and pasted each of the content the guy provided. However its not working.
Could someone kindly provide a reliable way to start an OSX App without
#NSApplicationMain ?

It looks like your code actually is working, but that there's a few other things you need to do to get it to work the way you want it to. You have one window that is being loaded from the MainMenu.xib in Interface Builder - that is the window with "911" in the title bar. You've got the second window that you are creating programmatically. That is the window in the lower left corner of the screen.
First, it looks like you still have a Window in your MainMenu.xib file. Click on MainMenu.xib, and when it opens in Interface Builder, look at the items on the left-hand pane. I'm guessing that there is a Window icon. You want to click on that and delete it.
After that you will just have the single window in the lower-left hand corner. It looks like a "view" that is unattached to a window because you haven't configured the window's styleMask property. You probably want a styleMask that looks like this:
let styleMask = NSTitledWindowMask
| NSClosableWindowMask
| NSMiniaturizableWindowMask
| NSResizableWindowMask
You say that the window also isn't where it is supposed to be. I don't know where you want it to be, but you can change its position and size by changing the contentRect parameter when you initialize it. Make a NSRect that positions and sizes your window where you want it, call it something convenient like myRect, and then initialize your window like this (this assumes you defined the styleMask constant from above):
newWindow = NSWindow(contentRect: myRect, styleMask: styleMask, backing: NSBackingStoreType.Buffered, defer: false)
That should do it. For an alternative version of main.swift, see this gist on github.

Related

Toolbar of NSScrollView inside SplitView [duplicate]

It's easy to enable the "inspector bar" for text views so that a bar appears at the top of the screen with various formatting buttons. (Although I had some confusion until I learned to make sure I was selecting the text view in a scroll view, and not the scroll view itself). I can either programmatically use [textView setUsesInspectorBar:YES] or go to the Attributes Inspector and check the "Inspector Bar" box in the "Uses" section.
My question is, how can I further control the inspector bar? I'm having trouble finding information on it in the XCode documentation or online. I'd like to be able to position it in a different place on the screen. Being able to pick and choose which specific controls are in the bar would be great too.
The answer is, you aren't meant to further control the inspector bar. There's nothing in the documentation because, well, there's nothing. Apple's saying, use it or don't use it.
However, if you dig into it a bit, you will find that the inspector bar is a very interesting control. It's not displayed as part of the text view, but rather (privately) embedded in the "window view" itself. When I say "window view," I mean the superview of the content view.
If you list the subviews of that "window view":
NSLog(#"%#", [self.testTextView.window.contentView superview].subviews);
You end up with:
2012-08-02 15:59:30.145 Example[16702:303] (
"<_NSThemeCloseWidget: 0x100523dc0>", // the close button
"<_NSThemeWidget: 0x100525ce0>", // the minimize button?
"<_NSThemeWidget: 0x100524e90>", // the maximize button?
"<NSView: 0x100512ad0>", // the content view
"<__NSInspectorBarView: 0x100529d50>", // the inspector view
"(<NSToolbarView: 0x10054e650>: FD2E0533-AB18-4E7E-905A-AC816CB80A26)" // the toolbar
)
As you can see, AppKit puts the inspector bar at the same level as other top level window controls. Now this is getting into the land of private APIs, but simply tinkering with the "window view" shouldn't get any apps rejected.
You can try to get a reference to the __NSInspectorBarView from here. It seems like it is always the subview right after the content view, so something like this may work:
NSArray *topLevelViews = [self.testTextView.window.contentView superview].subviews;
NSUInteger indexOfContentView = [topLevelViews indexOfObject:self.testTextView.window.contentView];
if (indexOfContentView + 1 < topLevelViews.count) {
NSView *inspectorBar = [topLevelViews objectAtIndex:indexOfContentView + 1];
NSLog(#"%#", inspectorBar);
}
NSLog(#"%#", topLevelViews);
Since this immediately breaks if Apple changes the ordering of the top level views, it may not be a good idea for an application for production. Another idea is:
NSView *inspectorBarView = nil;
for (NSView *topLevelView in topLevelViews) {
if ([topLevelView isKindOfClass:NSClassFromString(#"__NSInspectorBarView")]) {
inspectorBarView = topLevelView;
}
}
NSLog(#"%#", inspectorBarView);
I don't know if the use of NSClassFromString() will pass App Store review guidelines, however, since once again, it's dependent on private APIs.
That being said, once you get a reference to the inspector bar view, things still don't work too well. You can try repositioning it at the bottom:
if (inspectorBarView) {
NSRect newFrame = inspectorBarView.frame;
newFrame.origin = NSZeroPoint;
[inspectorBarView setAutoresizingMask:NSViewMaxYMargin | NSViewMaxXMargin];
[inspectorBarView setFrame:newFrame];
}
But you end up with a misdrawn toolbar, so more work would be necessary there:
My ideas would be to try to shift the content view's height up to cover up the gray left-over area (which would have to be done every time the window is resized, maybe tinkering with autoresizing masks may make it easier) and custom draw a background for the inspector bar at the bottom.
EDIT
Oh, and you should file a feature request for this too. bugreport.apple.com
This is four years late, but I feel like someone on the internet may benefit from this in the future. I spent way too long trying to figure this out.
The inspector bar class, as the others have pointed out, seems to be a private class (__NSInspectorBarView). Therefore, it's probably not recommended to modify.
Nevertheless! The curious have to know. The inspector bar is inserted, at the time of this post (April 2016) into the window's accessory bar. You can get a list of accessory views as of OS X 10.10 using the array property in NSWindow called titlebarAccessoryViewControllers[].
Here's some Swift 2.0 code to do just that, assuming you haven't inserted any other accessory views into the window beforehand.
if window.titlebarAccessoryViewControllers.count > 0 {
let textViewInspectorBar = self.titlebarAccessoryViewControllers[0].view
let inspectorBarHeight: CGFloat = textViewInspectorBar!.frame.height // 26.0 pt
}
It's worth noting that accessory views are handled differently in full screen mode apps: https://developer.apple.com/library/mac/documentation/General/Conceptual/MOSXAppProgrammingGuide/FullScreenApp/FullScreenApp.html
I personally would not attempt to move an accessory view, as they are special kinds of views designed to stay in the toolbar (if I fully understood what I have read).
NSTitlebarAccessoryViewController Reference:
https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSTitlebarAccessoryViewController_Class/
Another 3 years on, but I suspect some will find this useful. My specific problem was in having a window fully filled by a tabView - ideal for setting various kinds of user defaults. Only one of these tab pages had a couple of text views for which I wanted the inspector bar visible. Tabbing to that page made the inspector bar appear, and pushed the whole lot down, ruining my carefully planned layouts. Tabbing away from the page did not hide it again.
The obvious thing was to get the inspector bar to appear on the relevant tab page only. Having got hold of it ("on the shoulders of giants" - thanks to giant Vervious) it is relatively easy to reposition it in the view hierarchy. You are still left with the problem of space for an empty toolbar pushing the content down. The window's view hierarchy changes radically when the inspector bar first appears, and I gave up on trying to do anything with it.
My solution is to increase the content view's height. (Why height and not origin I can't say.)
func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
if let inspectorBar = window!.titlebarAccessoryViewControllers.first(where:
{$0.view.className == "__NSInspectorBarView"} )?.view {
// move content view back to where it should be
var sz = window!.contentView!.frame.size
sz.height = window!.frame.size.height - 21
window!.contentView?.setFrameSize(sz)
// put the inspector where we want it
inspectorBar.removeFromSuperview()
let y = textPage.frame.size.height - inspectorBar.frame.size.height - 10
inspectorBar.setFrameOrigin(NSPoint(x: 0, y: y))
textPage.subviews.insert(inspectorBar, at: 0)
}
}
The code belongs in a NSTabViewDelegate which I made my window controller conform to, remembering to set the tabView's delegate to File's Owner in the xib, and is called whenever a new tab is selected. textPage is the view inside the relevant tabViewItem.
There are some arbitrary constants found by trial and error. The function only need run once. Repeated calls are harmless, but you could put in a flag to make an early return from subsequent calls.
You cannot do anything to position this thing.
Clearly, the corruption noted by #Vervious is real, but only if you do not have an NSToolBar.
You see, this inspectorBar is sadly a mostly private and mostly (publicly) undocumented but awesome tool. And it is very much intended for use in a window that has an NSToolBar visible... go figure.
After you have a toolbar added to your view
Still with a toolbar but hidden, and inspector bar is cool
(as in via the view menu or the method it invokes, which is toggleToolBarShown: and is an NSResponder friendly message )
So it is obvious, no you cannot do much with this. It's design is poorly documented. It works as intended as a pseudo accessory view bar under the place an NSToolbar goes (which is also not adjustable)

OSX - How to identify window under cursor in all spaces including fullscreen

I'm using Window Services' CGWindowListCreate and CGWindowListCreateDescriptionFromArray to get window information. When getting kCGWindowBounds in a regular Space everything works fine (I'm drawing borders around the frontmost window on the 0th level). However, when I use the same method while on a fullscreen application's Space, I get nonsense bounds: (0, 855, 480, 1).
I wouldn't care much about this if there was an easy way to tell if I'm currently at a fullscreen app's Space, because then I'd just draw a border around the screen (well... it would depend if the menu bar is showing...).
Is this a bug, or is there a reason for this behavior?
EDIT:
Figured out my problem. It's a bigger issue than I would have liked. The thing is the API goes through ALL NSWindows, even the ones that aren't, well, normal windows. Chrome's loading bar on the bottom is a window by itself, for example, and Mail also has some window on the top of the app. This is a problem because I have no way to differentiate the window that looks to be frontmost.
For my app, I would like to capture a specific window to intercept mouse events in it. I would have liked to be able to have the user press a hotkey and then click on the desired window to select, but there is no API to get the window under the cursor. I have no clue how to proceed.
Edit 2:
To better help people find a useful answer, changed title from: "Quartz Window Services returning wrong window bounds for fullscreen apps"
Have you got these methods defined for the window delegate?
- (NSSize)window:(NSWindow *)window willUseFullScreenContentSize:(NSSize)proposedSize
{
NSRect mainDisplayRect = [[NSScreen mainScreen] frame];
CGSize cgScreenSize = CGSizeMake(mainDisplayRect.size.width, mainDisplayRect.size.height);
return cgScreenSize;
}
- (void)windowWillEnterFullScreen:(NSNotification *)notification
{
}
- (void)windowDidEnterFullScreen:(NSNotification *)notification
{
}
- (void)windowWillExitFullScreen:(NSNotification *)notification
{
}
I proceeded by going through the description dictionaries and checking if the current cursor position was inside the bounds of the windows. The first window to satisfy this would be the window right under the cursor, which is exactly what I needed.
Separately, to find the current top-most window, I used the iChat Apple example of the Accessibility API to register ApplicationActivatedNotification and MainWindowDidChangeNotifications. Both notifications combined would let me keep track of the main window of the active app (top-most). To get the bounds in this case, I just got the main window's position and size using the Accessibility API.

How can I get a two-row toolbar like in Mail.app and Xcode?

I'm trying to add a "second row" after my NSToolbar in my app, that remains part of the title bar. As an example, Mail has a thin gray divider line below the NSToolbar with some extras items below that. Very specifically, when the window is put into fullscreen mode, that second "row" stays attached to the title bar as it slides down under the system menu bar. Xcode has a similar story.
I tried setting my NSWindow to textured and placing my second row controls directly in the content view of the window. While this mostly looks correct in windowed mode, those controls of course won't appear attached to the toolbar when it slides down in fullscreen mode. So how can I achieve the same behavior that Mail and Xcode do? I've looked at a lot of toolbar customization code but none of them really cover this specific case.
fullScreenAccessoryView is deprecated in macOS 10.10
In order to do this in recent versions of macOS, use the addTitlebarAccessoryViewController method on your NSWindow and pass in a subclass of NSTitlebarAccessoryViewController.
For example:
NSTitlebarAccessoryViewController *accessoryViewController = [[NSStoryboard storyboardWithName:#"Main" bundle:nil] instantiateControllerWithIdentifier:#"AccessoryViewController"];
[self.mainWindowController.window addTitlebarAccessoryViewController:accessoryViewController];
What I needed to do was call [NSToolbar setFullScreenAccessoryView:] on the view below my toolbar. This results in the behavior I was aiming for. See the NSToolbar documentation for this method.
First one is normal toolbar. For second toolbar you can create a separate view of your desired height and add it in the main landing-window.

Child Windows Not Being Added Properly

I'm trying to add a secondary window to bottom of my main window in an OS X application, with the hopes of eventually making it so it animates out from underneath the window when a toggle button is pressed. As far as I can tell, the best way to do this with the SDK is to add a window as a child window using - [NSWindow addChildWindow:ordered:]. However, when I call that function, although the secondary window is displayed, it isn't added as a child window. Here's the function called when I press the button:
- (IBAction)childToggleButtonPressed:(id)sender {
[self.window addChildWindow:_secondaryWindowController.window ordered:NSWindowBelow];
NSLog(#"Child Windows: %#", [[self.window childWindows] count]);
}
(I haven't added the code to dismiss it yet because I'm making sure it shows up in the first place first.)
And here's the output to the console:
2011-08-31 12:37:25.312 Playground[1712:707] Child Windows: (null)
Does anyone know why the window isn't being added as a child and what I can do to fix this?
Some additional context that might help is that I'm drawing a custom window using an image as the background for both the window itself and the title bar. The code I'm modifying can be found at http://cocoawithlove.com/2008/12/drawing-custom-window-on-mac-os-x.html.
Thank you!
EDIT: I tried overriding the - [NSWindow addChildWindow:ordered:] function and logging any information I could find, and it turns out the window itself isn't passed to the function. Hopefully this will help someone find out the problem!
I ended up solving this problem by making it so that the child windows did not have NSWindowControllers. Apparently you can't assign the window of an NSWindowController to be a child window. As soon as I turned them into NSWindow subclasses, I could add them as child windows using the code I showed above (replacing _secondaryWindowController.window with the window, of course).
In short, don't use an NSWindowController's window as a child window, use just an NSWindow.

Making a Cocoa App Pop Up a Widget instead of a Window

I have never made an app using XCode and Cocoa but I think following these instruction:
http://developer.apple.com/Mac/library/documentation/GraphicsImaging/Reference/IKImagePicker_Class/IKImagePicker_Reference.html
I could easily make an app that pops up a window that allows you to push a button to bring up the IKPictureTaker, but this is not quite what I want. I want my app to just automatically bring up the PictureTaker. I assume to do this I would have to abandon the interface builder altogether and do the whole thing programatically but I can't figure out what call will tell the Cocoa app to use this class or method at start up. Where would this be done programatically? I am trying to do all this in a Cocoa app that will be run in OSX.
alt text http://www.freeimagehosting.net/image.php?38f459584c.png][img=http://www.freeimagehosting.net/uploads/th.38f459584c.png
You'll want to use the beginPictureTakerWithDelegate:didEndSelector:contextInfo: method, which will give you a stand-alone pictureTaker window.
IKPictureTaker *sharedPictureTaker = [IKPictureTaker pictureTaker];
[sharedPictureTaker setValue:[NSNumber numberWithBool:YES] forKey:IKPictureTakerShowEffectsKey];
[sharedPictureTaker beginPictureTakerWithDelegate:self didEndSelector:#selector(pictureTakerDidEnd:returnCode:contextInfo:) contextInfo:nil];
If you put that somewhere, like in your Application Delegate's applicationDidFinishLaunching: method, you'll get the picture taker window # startup.
If you want a more custom solution you can look into the QuicktimeKit. It's not as easy as the three liner posted above but it's relatively pain-free. You'll have much more flexibility in the look of your picture taker window, be able to select from any number of inputs, be able to add your own filters, etc. Could be worth a look.
I'm not totally familiar with the IKPictureTaker. If it does something I'm not crediting it then let me know.
The way to automatically instantiate a class in Xcode /Cocoa is, strangely enough, through Interface Builder (IB). Open your MainMenu.xib in IB and make sure you can see the Document window (menu Window >> Document). Now, in the Library expand Cocoa >> Objects & Controllers >> Controllers. You'll see a number of controller, among them a blue cube. Drag this blue cube to your MainMenu.xib document window. You'll see that File's Owner and Font Manager sport the same blue cubes as their logo. Now, select your blue cube and in the Inspector choose the Identitiy panel (letter i on blue circle). Set the Class to the class you created before in Xcode, and save MainMenu.xib.
When you run your program, your class will automatically be instantiated. Your starting point from where you can start calling other methods or instantiating other objects is
- (void)awakeFromNib
{
NSLog(#"%# I'm alive!", [self class]);
}

Resources