I have a document-based application with a view controller that contains a table, array controller and add/remove buttons. I have bound the Managed Object Context to File's Owner (myViewController) and established property/synthesize commands for myMOC.
My issue is how to set the managed object context from within the view controller. I wrote...
NSWindow *myWindow = [[self view] window];
NSWindowController *myWindowController = [myWindow windowController];
id myDoc = [myWindowController document];
self.myMOC = [myDoc managedObjectContext];
However, calling that from within awakeFromNib or init, generates a nil value for myWindow. Any help is appreciated. Thank you
I'm assuming that myView is in its own nib when you load it and then you're adding the view to a window that's created in a different nib due to myViewController being the file's owner. Is that correct? If so, [[self view] window] should be returning nil since the view hasn't been added to a window when either awakeFromNib or init get called. You would need to run myMOC set-up code after myView has been added to the window. Try breaking the above code out into a -setupMOC method and call it after you add the view to the window or superview. If you're doing it right away you might also be able to get away with calling [self performSelector:#selector(setupMOC) withObject:nil afterDelay:0.0] in awakeFromNib.
Related
In my application window I have two NSViews. On the left the NSView ("Menu") contains a few buttons. When one of the buttons is clicked it should change the contents of the right NSView ("Content").
For each of the views on the right I have a separate NSViewControllers that get loaded and their views gets added as a subview. When a further button gets pressed on the left the added subviews on the right should be removed and the new view should be loaded as a subview.
To accomplish this I load my Menu in AppDelegate with the following:
MenuVC *menuSubView = [[MenuVC alloc] initWithNibName:#"MenuVC" bundle: nil];
menuSubView.contentView = (NSView*)[self contentView];
[[self menuView] addSubview:[menuSubView view]];
This works fine. As you can see I have a NSView pointer in the Menu VC which points to the contentView so that I can populate it with the subviews.
Now as a method for one of the button presses I do the following:
SomeContentVC *subView = [[SomeContentVC alloc] initWithNibName:#"SomeContentVC" bundle:nil];
[self.contentView addSubview:[subView view]];
This does not work.
If I however add a subview from the awakeFromNib method of the MenuViewController implementation (in the case of default content when the app opens) it works. However when I try to remove that subview using
[[self.contentView setSubviews:[NSArray array]];
I can't. Interesting is also that if I try to count the number of subviews (even after having added one in the awakeFromNib method) it returns 0 subviews for self.contentView. Why? How can I get it to work properly?
Thanks
The fact that messaging self.contentView achieves nothing except, for some things, returning 0 probably means that self.contentView is nil.
Do you perhaps have two instances of MenuVC by accident? Perhaps one instantiated in a NIB and one instantiated in code?
When in doubt, log everything. Log self in various methods. Log menuSubView just after you create it. Log menuSubView.contentView just after you assign it. Etc. Eventually, you'll probably see that you're interacting with different objects than you thought you were.
I am building a cocoa application with one main window controller with a xib. That xib contains many custom view classes. I would like to add an NSViewController to the xib, but i'm running into some trouble.
In interface builder I can drag the NSViewController into the xib, assign it its custom controller class, and assign its view to the appropriate view in the xib. Here's the problem: neither the initWithNibName:Bundle: or loadView get called.
What am I missing?
EDIT:
People seem to be misunderstanding the question so I'll clarify.
The window already has a view controller. What I am seeking to do is assign separate view controllers to several of the subviews. I need to know how to associate my NSViewController subclass with the appropriate NSView subclass (which is a child of the main window).
Or in other words, I am trying to use multiple NSViewController subclasses to controll many different custom views (one each) within a single .xib file. Those controllers and subviews have their own .xibs which should ultimately become visible in the same window.
The pattern I use for NSViewController is to have a xib per view controller. Then, when you need that view controller you alloc it and use the initWithNibName:Bundle: method. As soon as you use its view, loadView will get called.
Example:
self.editViewController = [[[MyEditViewController alloc] initWithNibName:#"MyEditViewController" bundle: nil] autorelease];
[self.window setContentView: editViewController.view];
I used to get stuck with that as well and gave up on that thing - the blue circle with a white bordered view in it from the IB palette. I now create my controllers from code and only set a reference in IB to the owning controller class via the file owner: right click the file owner, enter the class name in the Identity inspector and then make a connection from the file's owner view to the view.
In your code you then do at an appropriate initialisation point:
[self setMyViewController = [[MyViewController alloc] initWithNibName: #"MyView" bundle: [NSBundle mainBundle]]
For your specific case this could be in windowDidLoad method when your window is loaded from its nib and ready for work. You can then add the view to your windows content view. Also you might want to consider to have a 1:1 relation between view and view controller. It makes life a lot easier in terms of maintenance.
EDIT: Like #pcperini suggests in his comments you can use the palette component, but you'll still need to instantiate the controller in your code. If you want to use the palette component, create a property in your main controller or AppDelegate:
#property (...) MyViewController *myViewController;
Add the line of code to actually create the controller (see above). Then, using the bindings inspector bind the palette component to the myViewController property.
So, what you are missing is that you are actually not instantiating the controller object.
EDIT 2: Here's the code (the awakeFromNib is the method of the top controller). It creates two child controllers each handling a different subview:
- (void) awakeFromNib {
[[self startEndTopicHeader] setHeader: #"Event timeline boundary"];
[[self startDateHeaderView] setHeader: #"Event (start) date"];
[[self endDateHeaderView] setHeader: #"Event end date"];
[self setStartDateViewController: [[EventTimeViewController alloc] initWithNibName: #"EventTimeView" bundle: [NSBundle mainBundle]]];
[[[self startDateViewController] view] setFrame: [[self dummyStartView] bounds]];
[[self dummyStartView] addSubview: [[self startDateViewController] view]];
[[self startDateViewController] setParentController: self];
[self setEndDateViewController: [[EventTimeViewController alloc] initWithNibName: #"EventTimeView" bundle: [NSBundle mainBundle]]];
[[[self endDateViewController] view] setFrame: [[self dummyEndView] bounds]];
[[self dummyEndView] addSubview: [[self endDateViewController] view]];
[[self endDateViewController] setParentController: self];
}
I have a UIViewController subclass that contains an instance of UIImagePickerController. Let's call this controller CameraController. Among other things, the CameraController manages the UIImagePickerController instance's overlayView, and other views, buttons, labels etc. that are displayed when the UIImagePickerController, let's call this instance photoPicker, is displayed as the modal controller.
The photoPicker's camera overlay and the elemets that are part of the CameraController view hierarchy display and function as expected. The problem I'm having is that I cannot use UIViewController's default initializer to create the CameraController's view heirarchy.
I am initializing CameraController from within another UIViewController. Let's call this controller the WebViewController. When the user clicks on a button in a view managed by WebViewController, the launchCamera method is called. It currently looks like this:
- (void) launchCamera{
if (!cameraController) {
cameraController = [[CameraController alloc] init];
// cameraController = [[CameraController alloc] initWithNibName:#"CameraController"
// bundle:[NSBundle mainBundle]];
cameraController.delegate = self;
}
[self presentModalViewController:cameraController.photoPicker animated:NO];
}
I want to be able to create CameraController by calling initWithNibName:bundle: but it's not working
as I'll explain.
CameraController's init method looks like this:
- (id) init {
if (self == [super init]) {
// Create and configure the image picker here...
// Load the UI elements for the camera overlay.
nibContents = [[NSBundle mainBundle] loadNibNamed:#"CameraController" owner:self options:nil];
[nibContents retain];
photoPicker.cameraOverlayView = overlay;
// More initialization code here...
}
return self;
}
The only way I can get the elements to load from the CameraController.xib file is to call loadNibNamed:owner:options:. Otherwise the camera takes over but no overlay nor other view components are displayed. It appears that a side-effect of this problem is that none of the view management methods on CameraController are ever called, like viewDidLoad, viewDidAppear etc.
However, all outlets defined in the nib seem to be working. For example, when the camera loads a view is displayed with some instructions for the user. On this view is a button to dismiss it. The button is declared in CameraController along with the method that is called that dismisses this instructions view. It is all wired together through the nib and works great. Furthermore, the button to take a picture is on the view that servers as photoPicker's overlay. This button and the method that is called when it's pressed is managed by CameraController and all wired up in the nib. It works fine too.
So what am I missing? Why can't I use UIViewController's default initializer to create the CameraController instance. And, why are none of CameraController's view mangement methods ever called.
Thanks.
Your problem is easy but need some steps.
Well... First, if overlay is an IBOutlet, it can not be loaded at init time. So move picker and co in viewDidLoad. Place also here all other items that your say that they are not loaded. They should be loaded there (viewDIDLoad). Check that outlets are connected.
Second, call
cameraController = [[CameraController alloc] initWithNibName:#"CameraController"
bundle:nil];
and ensure that CameraController contains (just) a view, and CameraController inherits UIViewController. Check also file's owner.
And at some time, you may consider that calling :
[self presentModalViewController:cameraController.photoPicker animated:NO];
does not make the CameraController control your picker. Does that make sense to you ?
What does that do regarding your problem ?
It seems you are confusing some things. I try to explain in another way :
The one that controls the picker is the one that is its delegate. Your may consider creating in a MAIN view.
The controller of the overlay (added as subview) is the one that own its view in File's Owner. That may be created from the MAIN view, adding its view as subview of the controller. Basically, it is loaded just to get the overlay, but viewDidLoad, ... won't be called.
That's all and I belive those steps are not ok in your code.
That should give something like :
MainController
Loadcamera {
self.picker = [UIImagePicker alloc] init.....];
self.picker.delegate = self;
SecondController* scnd = [[SecondController alloc] initWithNibName:#"SecondController" bundle:nil];
[self.picker addOverlay:scnd.view];
[self presentModalViewController:self.picker animated:NO];
}
/// And here manage your picker delegate methods
SecondController
// Here manage your IBActions and whatever you want for the overlay
In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow (I mean the serialised one that resides inside the XIB)?
What am i doing wrong here?
You should be using an NSWindowController subclass. NSWindowController is specifically designed to do exactly what you want to achieve and solves several problems that you will run into if you load the nib directly using the methods of NSBundle. You generally should always use an NSWindowController subclass to manage windows.
Create a subclass of NSWindowController:
#interface MyWindowController : NSWindowController {}
#end
#implementation MyWindowController
- (id)init
{
self = [super initWithWindowNibName:#"MyWindow"];
if(self)
{
//initialize stuff
}
return self;
}
//this is a simple override of -showWindow: to ensure the window is always centered
-(IBAction)showWindow:(id)sender
{
[super showWindow:sender];
[[self window] center];
}
#end
In Interface Builder, set the class of File's Owner to be MyWindowController and connect the window outlet of File's Owner to the window object in your nib.
You can then display the window by doing this:
MyWindowController* controller = [[MyWindowController alloc] init];
[controller showWindow:self];
In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow inside the XIB?
“centering the NSWindow inside the XIB” makes no sense (you would center it on the screen), unless you mean centering the NSWindow object that is inside the xib, in which case, why are you creating another NSWindow (CustomWindow) object outside of the xib?
Remember that a nib (or xib) is an archive of objects. If you want to use a window that you have in your nib, you need to create an outlet to point to that window, set the class of the File's Owner to be the class where you've added the outlet, hook up the outlet in IB, and appoint the object with the outlet as the File's Owner by passing it to the owner: argument. That object, as the owner, will then be responsible for working with the window. It may be (usually is, in my code) the same object that loads the nib.
Also, init doesn't work on NSWindow; you must use initWithContentRect:styleMask:backing:defer: or initWithContentRect:styleMask:backing:defer:screen:. Using init would only be valid if you've implemented init yourself in CustomWindow, and used one of those two selectors for the [super init…] message.
You probably don't want to make your window the File's Owner. Normally you would pass self or some controller object there. Then if self or that controller object has a CustomWindow IBOutlet, it will get hooked up when you call loadNibNamed:. Check out this post for example code.
A XIB is a container for objects, it's not equal to a window. You can't center a XIB, you can only center a window contained in a XIB.
Also, the objects in the XIB are created when you load it. You don't pass an object as owner that then stands in for one of the objects in the XIB, you instead use IBOutlets to get references to the new objects created when loading the XIB and then you can interact with them.
The File's Owner object is a special object in XIBs, as it's the only object that is not created and that you can specify by passing it to loadNibNamed:owner:. It's your gateway between the XIB-created objects and your application.
Usually, the owner object is some kind of controller class. Set the File's Owner's class in Interface Builder to your controller class, then define some IBOutlets in the class, they will show up in Interface Builder on the File's Owner and you can connect your objects in the XIB to them.
Finally, when you pass your controller object to loadNibNamed:owner:, Cocoa will connect your IBOutlets to the newly created objects and you can use them to interact with them, e.g. to center a window in your XIB.
I am trying to work my way through basic iPhone programming and I have a good basic understanding of how Interface Builder works, so I decided to try my hand at doing the views programmatically. I have gone through the ViewController Apple guide and searched everywhere and I cannot seem to find a solution to my problem. This leads me to believe it is a very simple solution, but I am just really banging my head against the wall. Basically all I am trying to do is create a view that gets main window as a subview. I know that if self.view is not defined then the loadView method is supposed to be called, and everything is supposed to be set up there. Here is the current state of my code:
The delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
StartMenuViewController *aViewController = [[StartMenuViewController alloc] init];
self.myViewController = aViewController;
[aViewController release];
UIView *controllersView = [myViewController view];
window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[window setBackgroundColor:[UIColor redColor]];
[window addSubview:controllersView];
[window makeKeyAndVisible];
}
The view controller:
- (id)init {
if (self = [super init]) {
self.title = #"Start Menu";
}
return self;
}
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
UIView *startView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
[startView setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth];
[startView setBackgroundColor:[UIColor greenColor]];
self.view = startView;
[startView release];
}
Thanks for the help in advance!
Are you sure that you're inheriting from UIViewController and not overriding the implementation of - (UIView*)view?
EDIT: More info:
UIViewController has a special implementation of the "-(UIView*) view" message so that when it's called, the loadView method is called if the view member variable is not set. So, if you provide an implementation of "- (id)view" in your subclass, (or a property named view) it will break the auto-calling of "- loadView".
Just to document a "loadView is not called" case:
I wrote a 2 UITableViewController(s) to handle detail data for a master ViewController. Since the devil was in #2, I made a simple UITableViewController for #1, and referenced it in the XIB for the "master" ViewController.
When I was done with #2, I could simply copy the code to #1, remove the complicated code, and go on with life.
But to my dismay and several days work, no matter what I did, viewLoad was not being called for my simple #1 UITableViewController.
Today I finally realised that I was referencing the UITableViewController in the XIB to the master ViewController program. - and of course, loadView was never being called.
Just to help some other dork that makes the same mistake....
Best Regards,
Charles
viewDidLoad only if the view is unarchived from a nib, method is invoked after view is set.
loadView only invoked when the view proberty is nil. use when creating views programmatically. default: create a UIView object with no subviews.
(void)loadView {
UIView *view = [[UIView alloc] initWithFrame:[UIScreen
mainScreen].applicationFrame];
[view setBackgroundColor:_color];
self.view = view;
[view release];
}
By implementing the loadView method, you hook into the default memory management behavior. If memory is low, a view controller may receive the didReceiveMemoryWarning message. The default implementation checks to see if the view is in use. If its view is not in the view hierarchy and the view controller implements the loadView method, its view is released. Later when the view is needed, the loadView method is invoked
again to create the view.
I would strongly recommend you use interface builder for at least your initial Window/View.
If you create a new project in XCode you should be able to select from one of many pre-defined iPhone templates that come with everything setup.
Unless I am reading this wrong, you did not associate any view with the the controller's view property like this
myViewController.view = controllersView;
So as far as Cocoa is concerned the view you are setting in the window has no controller to call loadView on. loadView is a View controller, not view, method. The view you assign to the window is not associated with any view controller. So your view controller loadView method is never called. Get it? The view you are trying to display, has no view controller associated with it.
When you use interface builder to create views you can link the UIView object you created in IB to the view property in the controller in IB which the framework automatically
But if not done in IB you have to set it