I am having a trouble try to display a NSWindow with out using Interface Builder. The initialization of the window was quite confusing since I am more familiar with iPhone (which does not have an NSWindow equivalent). So I searched Google for some code and I eventually found this:
NSRect windowRect = NSMakeRect(10.0f, 10.0f, 800.0f, 600.0f);
NSWindow *window = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:( NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask)
backing:NSBackingStoreBuffered defer:NO];
[window makeKeyAndOrderFront:nil];
So I copied that code and placed it in the applicationDidFinishLaunching and thought all would be good. But all is not good. Xcode did not display any errors (or warnings) in the Build Results. But, I do get this message in the display log:
2010-06-26 13:33:47.170 FooApp[283:a0f] Could not connect the action buttonPressed: to target of class NSApplication
I don't know how to interpret this as Google has failed me on searching for a solution on this display log error. And, as far as I can tell, I have no actions at the moment including a buttonPressed one. As a side note: I do not know if this is relevant or not, but I deleted the Main Window.xib and its accompanying property in the info.plist.
Any help would be greatly appreciated.
UPDATE: I tried doing some printf debugging (never really bothered learning NSLog) and the thing won't even printf if the thing is at the very beginning of the appliactionDidFinishLaunching or even worst, at the start of main (before the return if incase some of you are tempted to ask me if I put the printf before or after the return statement).
MainWindow.xib is part of the iphone App template, isn't it? What exactly did you delete? You still have the MainMenu.xib, right?
As you have discovered, having a nib file is not optional for a Cocoa app. You must have at least one nib (or xib, for you youngsters) and it must have a main menu in it.
Related
When I try launching my app through Spotlight without building it through XCode, the NSWindowController displays the unmodified nib (with the standard gray background color, etc) for a split second before windowDidLoad finishes. This looks awful, since views are in the wrong places and aren't colored correctly.
I tried removing everything from windowDidLoad to see if something in there was slowing it down, but that didn't improve things. I also tried moving the setting of the background color to initWithWindowNibName, but that didn't help either.
Is there a way to delay showing the window while it finishes loading?
Here's the code I'm using to initialize the NSWindowController:
self.windowController = [[WindowController alloc] initWithWindowNibName:#"WindowController"];
[self.windowController showWindow:self];
[[self.windowController window] makeKeyAndOrderFront:nil];
Disable NSWindowController's "visible at launch" property in Interface Builder.
(Of course, I post the question then immediately figure out the answer.)
I have 150 UIViewController in Storyboard and scrolling between these Views is so slow. I can't zoom in and zoom out easily and it takes some serious time to do sty.
I'm on MBPR and I installed Xcode 4.4
Spec: 2.3GHz / 16G / 256 which I think it's enough to handle such a thing.
Is there any options, settings, or tips/tricks to have so many views in storyboard and don't miss the performance.
NOTE: I've done all the possible solutions (deleting cache and workspace). Didn't work. It has something to do with number of UIViewController in Storyboard.
Thanks
Update 2016: Just to update this question as there is a new feature in Xcode 7 that allows you to refactor the Storyboard into multiple Storyboards.
Refactoring Storyboards
https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/Chapters/RefactorStoryboard.html
If you search the term "refactoring storyboards" you will find good tutorials :)
It is considered best practice to split up storyboards into lots of different modules (each one in a separate storyboard). It will take away these performance issues you are having and also has other advantages such as making it easier to manage in general (no massive SVN conflicts etc).
However I had another issue which was causing storyboard lag. I had approx 25 view controller and was receiving ALOT of lag - but only when Xcode was running on an external monitor.
I noticed that if I disabled "auto layout" for the storyboard, the lag would completely disappeared. I reverted this change, and then followed the following process:
-Delete a ViewController
-test if it still lags
-if still laggy revert changes
Eventually I found a certain ViewController which if deleted stopped all lag. I then reverted this and went through the views to see which view caused the lag. I eventually narrowed this down to a "UIButton" inside a "UIBarButtonItem". I believe I changed the "Type" property on the button, and then changed it back and the lag stopped. From SVN it seems like the frame was changed in the .storyboard file. After this point the lag never came back again.
TLDR:
Storyboard lag is not always because you have too many items in the storyboard. I managed to get rid of a lag problem by causing Xcode to re-do some layout.
Hope my experience will help someone else diagnose/solve their problems. I was working for about 0.5 years before I finally got really annoyed and tried to solve the issue.
Definitely use multiple storyboards for this. I am not aware of any limitations in using storyboards but trying to render all those UI code at one time is hard for your machine and it is also hard for developers to understand.
Try to logically divine your storyboards into categories such as: "profileSB, feedSB, mapSB, messagesSB, settingsSB"
Here are some good tutorials on the creation of these:
http://spin.atomicobject.com/2014/02/18/ios-storyboards-xcode5/
http://www.skillmasters.net/main/xcode-using-multiple-storyboards/
150 ViewControllers in one Storyboard sounds awful lot to me.
Try to minimize your flow or split into multiple storyboards if you really need so many
I had a UIStoryboard with 10 or more UIViewControllers and additional ContainerViews. After layouting the views and customizing more and more, the UIStoryboard got more and more lazy.
My approach was to setup the views inside single UIStoryboards. Loading the controllers is done inside my Menu, where I setup an NSArray with all identifiers for the UIViewController which also have to be setup inside the UIStoryboard:
When loading the menu, I loop through the NSArray and load the UIViewControllers by identifiers from the specific UIStoryboard. This is the place where I needed to implement a switch, for the different UIStoryboards:
self.arrayVCAll = [NSMutableArray new];
for ( NSArray *array in _arrayViewControllerAll ){
NSMutableArray *arrayTemp = [NSMutableArray new];
for (UIViewController *vc in array ){
NSString *strViewController = [NSString stringWithFormat:#"%#", vc];
UIStoryboard *storyboard;
if( [strViewController isEqualToString:#"CustomOneStoryboard"] ){
storyboard = [UIStoryboard storyboardWithName:#"FirstVC" bundle:nil];
} else if( [strViewController isEqualToString:#"CustomTwoStoryboard"] ){
storyboard = [UIStoryboard storyboardWithName:#"SecondVC" bundle:nil];
} else {
storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
}
UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:strViewController];
MyNavController *nav = [[MyNavController alloc] initWithRootViewController:controller];
[arrayTemp addObject:nav];
}
[self.arrayVCAll addObject:arrayTemp];
}
In my case, there was just a problem with the segues after separating the initial UINavigationController from my UIViewControllers. The segues won't push to a navigationController, if there is no initial UINavigationController. Thats why I added a UINavigationController on each UIViewController (of my NSArray) so the UIStoryboardSegue will be done correctly. The UINavigationController also doesn't need to be connected to a class, just include it inside the UIStoryboard and connect it to the first UIViewController.
In most systems, the default behaviour for "open a new window" is that it appears at the front. This doesn't happen in Cocoa, and I'm trying to find the "correct" way to make this standard behaviour. Most things I've tried only work for a maximum of one window.
I need to open multiple windows on startup:
(N x NSDocuments (one window each)
1 x simple NSWindowController that opens a NIB file.
Things that DON'T work:
Iterate across all the NSDocuments I want to open, and open them.
What happens? ... only the "last" one that call open on comes to the front - the rest are hidden, invisible, nowhere on the screen, until you fast-switch or use the Window menu to find them.
Code:
...documents is an array of NSPersistentDocument's, loaded from CoreData...
[NSDocumentController sharedDocumentController];
[controller openDocumentWithContentsOfURL:[documents objectAtIndex:0] display:YES error:&error];
Manually invoking "makeKeyAndOrderFront" on each window, after it's opened
What happens? nothing different. But the only way I can find to get the NSWindow instance is so horribly hacky it seems totally wrong (but is mentioend in several blogs and mailing list posts)
Code:
[NSDocumentController sharedDocumentController];
NSDocument* openedDocument = [controller openDocumentWithContentsOfURL:[documents objectAtIndex:0] display:YES error:&error];
[[[[openedDocument windowControllers] objectAtIndex:0] window] makeKeyAndOrderFront:nil];
...I know I'm doing this wrong, but I can't find out why/what to do differently :(.
Something that works, usually, but not always:
As above, but just use "showWindow" instead (I took this from the NSDocument guide).
Bizarrely, this sometimes works ... even though it's the exact code that Apple claims they're calling internally. If they're calling it internally, why does it behave different if I re-invoke it after they've already done so?
[[[openedDocument windowControllers] objectAtIndex:0] showWindow:self];
You can just open all the documents without displaying and then tell the documents to show their windows:
NSArray* docs = [NSArray arrayWithObjects:#"doc1.rtf", #"doc2.rtf",#"doc3.rtf",#"doc4.rtf",nil];
for(NSString* doc in docs)
{
NSURL* url = [NSURL fileURLWithPath:[[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:doc]];
NSError* err;
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:NO error:&err];
}
[[[NSDocumentController sharedDocumentController] documents] makeObjectsPerformSelector:#selector(showWindows)];
Won't this work?
For 10.6 or greater
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
This often has something to do with the app itself: your other windows are behind other apps (in particular, behind Xcode!), and would have appeared with a Hide Others command.
The solution to that problem would be that after you send showWindow to all of your windows (making sure you do the key one last), you tell the app to come forward, relative to other apps.
NSApp.activateIgnoringOtherApps(true) // Swift
or
[NSApp activateIgnoringOtherApps:YES]; // Objective-C
See also: How to bring NSWindow to front and to the current Space?
How can I load a nib inside of another window?
I tried initWithWindowName,
if (mmController == NULL)
mmController = [[mainMenu alloc] initWithWindowNibName:#"mainMenu"];
[mmController showWindow:self];
but it opens a new window.
I also tried loadNibNamed
[NSBundle loadNibNamed:#"mainGame" owner:self];
and it succeeded, but when I try to use the same method to get back to the main menu,
[NSBundle loadNibNamed:#"mainMenu" owner:self];
it doesn't work. It does nothing at all...
Any ideas?
I tried initWithWindowName,
You mean initWithWindow¹Nib²Name³:, which takes the name (3) of a nib (2) containing a window (1).
if (mmController == NULL)
This should be nil, not NULL, since you are comparing an Objective-C object pointer.
mmController = [[mainMenu alloc] initWithWindowNibName:#"mainMenu"];
What is mainMenu here? It must be a class, but what is it a subclass of?
[mmController showWindow:self];
From this message and the previous message, I'm guessing mainMenu is a subclass of NSWindowController.
Guessing should not be required. You should name your classes specifically, so that anybody can tell what the class is and its instances are merely by the class name.
Brevity is a virtue, but if you need to go long, go long. We've got modern tools with name completion. The tab key can eliminate the sole advantage of an abbreviated name.
but it opens a new window.
Yes. You created a window by loading it from a nib, and then you told the window controller to show that window. Showing a new window is the expected result.
I also tried loadNibNamed
[NSBundle loadNibNamed:#"mainGame" owner:self];
and it succeeded, but when I try to use the same method to get back to the main menu,
There is no “get back”. Loading a nib is simply creating objects by loading them from an archive. You can load the same nib multiple times, and loading a nib does not somehow undo the results of loading a previous nib.
You may want to read the Resource Programming Guide, which covers nibs as well as image and sound files, and the Bundle Programming Guide.
If you want to hide the window you loaded from the mainGame nib, do that. The term for this in AppKit is “ordering out” (as opposed to “ordering in”, which “ordering front” and “ordering back” are specific ways of doing).
[NSBundle loadNibNamed:#"mainMenu" owner:self];
it doesn't work. It does nothing at all...
Are you trying to load the MainMenu nib that came with your project? If so, make sure you get the case right—you don't want your app to be broken for people who run it from a case-sensitive volume, nor do you want it to be broken for people who use the default case-insensitive file-system.
If that's not what you're trying to do, then it isn't clear what you are trying to do. MainMenu is normally the nib containing the main menu (the contents of the menu bar); naming any other nib “mainMenu” or anything like that is going to cause confusion at best and problems at worst. If this is supposed to be some other nib, you should give it a different name.
Either way, this is not what you need to do. If you want to hide the window you loaded from mainGame, then you need to hide that window, not load a different nib.
Moreover, once the window is loaded, do not load it again (unless you close and release it). Once you have loaded it, you can simply order it back in. Most probably, you will want to both make it key and order it front.
On the Mac, you are not limited to one window at a time; indeed, your app has multiple windows (at least three), no matter what you do. The APIs are built around your ability to show multiple windows.
See the Window Programming Guide for more information.
How can I load a nib inside of another window?
As Justin Meiners already told you, you may want NSViewController for that, although you can go without and just load the nib containing the view directly using loadNibNamed:.
Be warned that NSViewController is not nearly as powerful/featureful as Cocoa Touch's UIViewController.
You'll want to read the View Programming Guide for this.
I'm trying to write some absolutely barebones code where I can drag a plain 'dot.txt' file onto an NSWindow and read in the data (and nothing fancier than that), but all the examples I've been able to find use images and NSViews etc.. Apple's 'Dragging File Contents' section in its "Drag and Drop Programming Topics for Cocoa" documentation confirms that dragging onto a plain NSWindow (rather than into an NSView etc.) is possible and seems to discuss exactly what I'm trying to do, but as a relative newbie I still find its reference to images and frames confusing.
Can anyone please help me get started by showing me where to 'registerForDraggedTypes' other than putting it in say, an 'initWithFrame' or 'initWithCoder' method, and what types to register for? Once I get the window to recognise my drag I can worry about the other 'performDragOperation' and 'draggingEntered' stuff later.
Thanks :-)
This is a part of code which I'm working on.
You can find this method when you created a new project.
-(void)applicationDidFinishLaunching:(NSNotification*)aNotification
{
[window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
}
I'm newbie too. Anyway, this worked for me.
And then, this is important.
We have to connect this object to window object to handle drop messages in this object
Open MainMenu.xib, and Interface Builder will pop up.
In Interface Builder, connect 'App Delegate'(this object) to 'delegate' outlet of 'Window'.
(You can find 'delegate' outlet in Inspector panel)
Now, 'App Delegate' receives event messages of 'Window'.
And then, adds more method at the same class file:
-(NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
{
return NSDragOperationGeneric;
}
-(BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
{
NSPasteboard* pbrd = [sender draggingPasteboard];
// Do something here.
return YES;
}