I am going through several example code banks and tutorials and just completely stumbling, a lot of it is due to the fact that these tutorials are written for Xcode < 4.2 and ARC changes a few things.
I am attempting to build an interface with an MVC design pattern. I am using the basic template provided for the Application Delegate. I have added a class called MainWindowController which inherits from NSWindowController. In the Interface Builder, firstly I removed the Window object in the MainMenu nib file (because I want it in a separate file). I create a new Interface called MainWindow(.xib) I change the file owner to MainWindowController I add the Delegate to the Object List. Now at this point something is not clicking.
I do not fully grasp how or what I need to implement in order for the Delegate to essentially load and launch the Window Controller. First I tried linking the outlet for "delegate" in the Window to the actual application delegate (called AppDelegate) and then linking the Window Outlet in the Delegate class to the Window in Interface Builder.
I would like an answer to this but I would be far more happy with the correct documentation describing this process. I'm sure there is something on MacDev but I'm having trouble finding it.
Here's what I'm working with:
#class MainWindow;
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (strong) MainWindow *mainWindowController;
#property (assign) IBOutlet NSWindow *window;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (IBAction)saveAction:(id)sender;
#end
...
#implementation AppDelegate
#synthesize window;
#synthesize mainWindowController;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize managedObjectContext = __managedObjectContext;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MainWindow *controller = [[MainWindow alloc] initWithWindowNibName:#"MainWindow"];
mainWindowController = controller;
// ... the rest handles the ManagedObject Models...
Solution to date:
#synthesize mainWindowController = _mainWindowController; // IBOutlet is linked in IB
//...
- (void) applicationDidFinishLaunching: (NSNotification *) aNotification
{
//... initialization of event handling etc...
if ( !_mainWindowController ) _mainWindowController = [[ MainWindowController alloc] initWithWindowNibName:#"MainWindow"];
[_mainWindowController showWindow: self];
// ...
The application delegate can manage an NSWindow in the main nib file or it can delegate that task to a controller (NSWindowController) which is typical of document based applications or MVC design patterns. The default nib file specified by the Basic-info.plist defines which nib file is loaded. Typically the default nib should be the main menu, which loads the delegate as well. The delegate should then by means of applicationDidFinishLoading: or awakeFromNib: perform initialization of the controllers and continue appropriate delegation in accordance with the delegation and mvc design patterns.
The main problem I was having was launching the window, which is done by showWindow: . The exacerbation of this problem stemmed from source code from a very old Mac OS X project that used deprecated functions and methods to accomplish delegation and led me down the wrong path. Thanks for the answer, it ultimately had me look in the right place for the right questions and I found the right answer.
Your problem is that in that second xib, "MainWindow", a new AppDelegate object is being created that has absolutely nothing to do with the AppDelegate object in the "MainMenu" xib. The objects in each xib are real objects that get serialized and then loaded at runtime.
This specific issue will be addressed in the Resource Management Guide, "Nib Loading". I can also suggest "Core Application Design".
Related
The first UITableView is presented inside a Popover that is called from the RootViewController of the application.
I need to navigate to another UITableView inside the same popover. This is easy to do if you just instance an object of the second UITableView and push it from the first one.
In the next paragraph I write as taking for granted some facts, please correct me if I'm wrong.
The problem here is that this process should be done inside the appDelegate. This is because I'm implementing Dropbox API and I need the pushViewController to be done immediately after the login process is done, which means the navigation through UITableViews has to be done inside of the application:handleOpenURL. I asume that application:handleOpenURL has to be called right there and that's why I also asume the pushViewController has to be done there in order to have the navigation done after the Dropbox API validation window is presented, without having to make the user do anything else.
This is how my code looks like:
AppDelegate.h
#interface AppDelegate : NSObject <UIApplicationDelegate>{
UINavigationController *navigationController;
NSString *relinkUserId;
UIWindow *window;
TableViewControllerForStorageList *rootViewController;
ViewController *viewController;
}
#property (nonatomic, strong) IBOutlet UIWindow *window;
#property (nonatomic, strong) IBOutlet UINavigationController *navigationController;
#property (nonatomic, strong) IBOutlet TableViewControllerForStorageList *rootViewController;
#property (nonatomic, strong) IBOutlet ViewController *viewController;
AppDelegate.m
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
[(TableViewControllerForStorageList *)self.window.rootViewController PushView];
}
return YES;
}
return NO;
}
TableViewControllerForStorageList.h
-(void)PushView;
TableViewControllerForStorageLost.m
-(void)PushView
{
TableViewControllerIpadStorage *tableViewControllerIpadStorage = [[TableViewControllerIpadStorage alloc]initWithNibName:#"TableViewControllerIpadStorage" bundle:Nil];
[self.navigationController pushViewController:tableViewControllerIpadStorage animated:YES];
}
Off course I got sure that Application:HandleOpenURL is running, but when calling PushView from there the error is [ViewController PushView]: unrecognized selector sent to instance
So, how can make the navigation be done from there? Which basics about objective c am I missing?
It is not clear from your question how your app is structured, so this answer may not be the best solution for your problem but hopefully it gives you some idea of how your view controller hierarchy is likely built up from your app delegate.
Lets say your first view controller class is named FirstViewController. Either your app delegate has a direct reference to an instance of this view controller, or it can access it through a parent view controller (perhaps via window.rootViewController).
Now lets say you have a method in FirstViewController named pushNextViewController that performs the task of pushing the second table view controller.
You can call that method from within the application:handleOpenURL: method of your app delegate.
This might look something like:
[self.window.rootViewController.firstViewController pushNextViewController];
There are other ways you could get a reference to your instance of FirstViewController and it would be cleaner if your rootViewController was a custom subclass so your could create a pushNextViewController method there and from that method tell your FirstViewController instance to pushNextViewController:
[self.window.rootViewController pushNextViewController];
Note that in both examples above, you will need to cast the rootViewController to whatever class it is actually an instance of or the compiler will warn you that it does not have the property firstViewController (example 1) or the method pushNextViewController (example 2).
EDIT: If your rootViewController is a UINavigationController, then your code might look more like:
UINavigationController* navController = (UINavigationController*)window.rootViewController;
FirstViewController* vc = navController.viewControllers[0];
[vc pushNextViewController];
EDIT 2: OK, It looks like the confusion here is that the window object has a rootViewController property (which appears to be pointing to your navigationController) and then you also have a rootViewController instance variable in your app delegate. These are two different objects, making your naming convention a bit confusing, but if I am right then the following should work:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
[rootViewController PushView];
}
return YES;
}
return NO;
}
You should consider changing the name of your app delegate's reference to your TableViewControllerForStorageList to something other than rootViewController to alleviate some confusion.
How to do it? I simply want to load a window and show it in front of the main window.
NSWindowController* controller = [[NSWindowController alloc] initWithWindowNibName: #"MyWindow"];
NSWindow* myWindow = [controller window];
[myWindow makeKeyAndOrderFront: nil];
This code shows the window for one moment and then hides it. IMHO this is because I don't keep reference to the window (I use ARC). [NSApp runModalForWindow: myWindow]; works perfectly but I don't need to show it modally.
Yes, with ARC if you don't hold a reference to the window it will be torn down as soon you as you exit the routine you were in. You need to hold a strong reference to it in an ivar. [NSApp runModalForWindow: myWindow] is different because the NSApplication object holds a reference to the window as long as it is being run modally.
You should likely do something similar to the following, which creates a strong reference to the NSWindowController instance you create:
.h:
#class MDWindowController;
#interface MDAppDelegate : NSObject <NSApplicationDelegate> {
__weak IBOutlet NSWindow *window;
MDWindowController *windowController;
}
#property (weak) IBOutlet NSWindow *window;
#property (strong) MDWindowController *windowController;
- (IBAction)showSecondWindow:(id)sender;
#end
.m:
#import "MDAppDelegate.h"
#import "MDWindowController.h"
#implementation MDAppDelegate
#synthesize window;
#synthesize windowController;
- (IBAction)showSecondWindow:(id)sender {
if (windowController == nil) windowController =
[[MDWindowController alloc] init];
[windowController showWindow:nil];
}
#end
Note that rather than sending the makeKeyAndOrderFront: method directly to the NSWindowController's NSWindow, you can just use NSWindowController's built-in showWindow: method.
While the above code (and sample project below) use a custom subclass of NSWindowController, you also use a generic NSWindowController and create the instance using initWithWindowNibName: (just make sure the File's Owner of the nib file is set to NSWindowController rather than a custom subclass like MDWindowController).
Sample project:
http://www.markdouma.com/developer/MDWindowController.zip
I have a simple Cocoa app. It has two windows, each in a separate xib file:
MainMenu.xib
SecondaryWindow.xib
I have an AppDelegate class, which has a reference to the window in MainMenu.xib. I'm trying to make it have a reference to the window in SecondaryWindow.xib. I'm confused about how to do this. I have made an outlet, as such:
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (assign) IBOutlet NSWindow *secondaryWindow;
#end
Here's the implementation:
#implementation AppDelegate
#synthesize window = _window;
#synthesize secondaryWindow = _secondaryWindow;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSBundle loadNibNamed:#"SecondaryWindow" owner:self];
NSLog(#"_window = %#", _window);
NSLog(#"_secondaryWindow = %#", _secondaryWindow);
}
#end
_secondaryWindow is always (null)
I've add an outlet from in SecondaryWindow.xib connection the second window to the outlet in AppDelegate. What else do I need to do in SecondaryWindow.xib to make the connection complete?
EDIT: added [NSBundle loadNibNamed...]
You need to load it...
By default, MainWindow.xib is loaded by the framework, which creates its own instance of the app delegate.
You should load your second window from your app delegate (try [NSBundle laodNibNamed:#"SecondaryWindow" owner:self]. when you do this, the File's owner will be the application delegate - change the class of the file's owner in interface builder to reflect that and make your connections to it)
Did you set the type of File's Owner in the secondary window's .xib to the type of your app delegate? And did you then connect the window in that .xib to the secondaryWindow outlet of File's Owner?
If you did those things, and if the .xib is properly included in the project and you've specified the name of the file correctly in the +loadNibNamed:owner: message, then your secondaryWindow property should be populated.
I am at a place in my application where essentially every ViewController has a local NSManagedObjectContext:
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
and every segue passes the managedObjectContext via the same setter
[segue.destinationViewController setManagedObjectContext:self.managedObjectContext];
Coming from Java, it would be easy to create an abstract class that each ViewController implementes. In Objective-c it doesnt seem like that is possible. What I am looking to do is have a base class that performs this passing, but basically anything that implements UIViewController will have this (including just a plain UIViewController as well as a UITableViewController). Would it be possible/correct to have create an "abstract" class that poses as UIViewController that does this?
Update:
UIViewController+ManagedObjectContext.h
#interface UIViewController (ManagedObjectContext)
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#end
UIViewController+ManagedObjectContext.m
#import "UIViewController+ManagedObjectContext.h"
#implementation UIViewController (ManagedObjectContext){
NSManagedObjectContext *context; // This is not valid, cant have local variables
}
#synthesize managedObjectContext; // This is not valid, must be #dynamic
-(void)setManagedObjectContext:(NSManagedObjectContext *)context{
//How do you have a local NSManagedObjectContext?
}
#end
You can just make your own subclass of UIViewController, let's say MOCViewController, with the managedObjectContext property. Then make all of your other view controllers be subclasses of MOCViewController instead of directly subclassing UIViewController.
If you really want to do it with a category, your category can use objc_setAssociatedObject to attach the managed object context to the view controller.
If you only really have one managed object context and you're just passing it around everywhere, consider just putting the context in a property of your application delegate, or in a global variable.
You can get the managedObjectContext from a managed object rather than pass it separately.
Generally its more logical to pass the managed object.
For example:
Say you have a managed object called thing, you can get the managedObjectContext by calling
NSManagedObjectContext *moc=[thing managedObjectContext];
Alternatively you can get the managed object context from the application delegate:
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
All,
In Apple's sample code "DateCell"
http://developer.apple.com/library/ios/#samplecode/DateCell/Introduction/Intro.html
the ivar "pickerView" is declared in MyTableViewController.h like this:
#interface MyTableViewController : UITableViewController
{
#private
UIDatePicker *pickerView;
UIBarButtonItem *doneButton; // this button appears only when the date picker is open
NSArray *dataArray;
NSDateFormatter *dateFormatter;
}
#property (nonatomic, retain) IBOutlet UIDatePicker *pickerView;
...
It is synthesized in the class file MyTableViewController.m like this:
#implementation MyTableViewController
#synthesize pickerView, doneButton, dataArray, dateFormatter;
...
When this app runs, I can insert NSLog(#"%#",pickerView) into ViewDidLoad and see that, sure enough, the ivar pickerView is real and has a value. Nowhere, though, does this class alloc/init pickerView. And that's the root of the question: how's it getting done if it's not being done explicitly?
Well, I naively copied this stuff to my code into my RootViewController.h and .m files figuring I could do the same, but pickerView stubbornly remains uninitialized (and my NSLog calls return "(nil)" as its value) no matter what I try short of explicitly alloc/initing it. Certainly RootViewController is being instantiated, or the RootView wouldn't be showing up, right? So shouldn't my pickerView be coming along for the ride just as it does for Apple?
So... do I have to manually alloc/init the pickerView instance variable? If so, where's Apple doing it? Or how are they doing it somehow otherwise?
I think I'm missing something very basic here, but I have no idea what it is. I can't see anything in Interface Builder or XCode that looks different between mine and theirs, but I've got tunnel vision at this point and can't see anything clearly anymore.
Thanks,
Bill
The IBOutlet modifier on this line is the key...
#property (nonatomic, retain) IBOutlet UIDatePicker *pickerView;
IBOutlet is a decorator that indicates that the object will be hooked up/connected/initialised when the corresponding xib (Interface Builder) file is loaded. The sample application you're looking up will contain a UITableViewController is a xib which has a connection to a UIPickerView.
You can either go the route of creating your own custom xib file and wire to an instance of UIPickerView or you can manually initialise the picker yourself.
Interface Builder (nib or xib) treats automatically IBOutlet ivar with connection of components.
IBOutlet is a special keyword that is
used only to tell Interface Builder to
treat an instance variable or property
as an outlet. It’s actually defined as
nothing so it has no effect at compile
time.
Your First iOS Application - The
View Controller Interface
Declaration, Making Connections
Interface Builder User Guide -
Defining Outlets and Actions in
Xcode