How to use NSViewController in Cocoa application based on NSDocument

I have a lot of experience with iOS, but Cocoa confuses me a bit. I read several Apple Cocoa docs, but there are more details that I couldn't find anywhere else. It seems that the documentation was written before the NSDocument-based Xcode template was updated to use NSViewController, so I don’t understand how exactly I should organize my application. The template creates a storyboard with NSWindow, NSViewController.

I understand that I should probably subclass NSWindowController or NSWindow to have a reference to my model object, and set this to makeWindowControllers (). But if I wanted to use the NSViewController instead of just putting everything in the window, I would also need to access my model. I notice that in my view controller there is something called a represented object that looks like it holds some model object (and then be thrown), but always it is zero. How to install it?

I find it difficult to formulate this question correctly, but I think I'm asking: how to use NSViewController in my application-based document?

PS: I understand that NSWindowController is usually designed to manage multiple windows that act on a single document, so presumably if I need only one window, then I don't need NSWindowController. However, requirements can change, and using NSWindowController may be better in the long run, right?

+6
source share
2 answers

I did not dive into the storyboards, but here's how it works:

If your application must support 10.9 and below create a custom subclass of NSWindowController

Document Based Application

Put this code in a subclass of NSDocument

- (void)makeWindowControllers { CustomWindowController *controller = [[CustomWindowController alloc] init]; [self addWindowController:controller]; } 

If your application has several windows, than adding them here or somewhere else (loaded on demand), but do not forget to add it to the windowscontroller document array (addWindowController :)

If you create them, but do not want to show all windows, then redefine

 - (void)showWindows { [controller showWindow:nil] } 

You can access your model at any time in your window controller.

 - (CustomDocument *)document { return [self document]; } 

Use the bindings in your window controller (subclass of the windowcontroller subclass + document in the key path, which is a property of the window controller)

 [self.textView bind:@"editable" toObject:self withKeyPath:@"document.readOnly" options:@{NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName}]; 

Unlike iOS, most views are on the screen, so you have to rely on templates: delegation, notification, events (responder chain) and, of course, MVC.

10.10 Yosemite Changes:

Starting from 10.10 , NSViewController is automatically added to the responder chain (usually the purpose of the action is unknown | NSApp sendAction: to: from :) and all delegates, such as viewDidLoad ... familiar with iOS, are finally implemented. This means that I no longer see the big benefits of subclassing NSWindowCotroller.

A subclass of NSDocument is required and NSViewController is sufficient.

You can access data at any time in the view controller

 - (CustomDocument *)document { return (CustomDocument *)[[NSDocumentController sharedDocumentController] documentForWindow:[[self view] window]]; //doesn't work if you do template approach //NSWindowController *controller = [[[self view] window] windowController]; //CustomDocument *document = [controller document]; } 

If you do this (according to KVC / KVO), you can do the binding as above.

Tips: Correctly implement UNDO for your model objects in a document, for example. or shamefully call updateChangeCount:

 [[self.undoManager prepareWithInvocationTarget:self] deleteRowsAtIndexes:insertedIndexes]; 

Do not put code associated with views / windows in a document

Split your application into several NSViewControllers, for example.

 - (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:AAPLListWindowControllerShowAddItemViewControllerSegueIdentifier]) { AAPLListViewController *listViewController = (AAPLListViewController *)self.window.contentViewController; AAPLAddItemViewController *addItemViewController = segue.destinationController; addItemViewController.delegate = listViewController; } } 

The previous code is called in the windowcontroller with the viewcontroller as a delegate (again only possible after 10.10)

I always prefer to use multiple XIBs rather than one giant storyboard / XIB. Use the following subclass of NSViewController and always inherit it:

 #import <Cocoa/Cocoa.h> @interface MyViewController : NSViewController @property(strong) IBOutlet NSView *viewToSubstitute; @end #import "MyViewController.h" @interface MyViewController () @end @implementation MyViewController - (void)awakeFromNib { NSView *view = [self viewToSubstitute]; if (view) { [self setViewToSubstitute:nil]; [[self view] setFrame:[view frame]]; [[self view] setAutoresizingMask:[view autoresizingMask]]; [[view superview] replaceSubview:view with:[self view]]; } } @end 
  • Add a subclass of MyViewController to the project using XIB. Rename XIB
  • Add NSViewController to XIB and change its subclass name Howto2
  • Change the XIB name of the boot to the name from step 1 Howto3
  • Link to replace the view you want to replace Howto1 Example Example Example Project Multi XIB

Inhale yourself shapeart or lister or TextEdit

And the real guide is to use Hopper and see how other applications are made.

PS: You can add your views / viewcontroller to the responder chain manually.

PS2: If you are a beginner, do not reverse engineer. Be pleased that your application works.

+13
source

I am relatively new to this myself, but I hope I can add a little understanding.

You can use view controllers in the same way as in ios. You can set points and goals, etc. For NSDocument-based applications, you can use a view controller or window controller, but I think that for most applications you end up using both options, most of which are in the view controller. Put logic wherever it makes sense. For example, if your nsdocument can have several types of windows, use a view controller for the logic specific to each type and a window controller for the logic that applies to all types.

The visibleObject property is primarily associated with Cocoa bindings. While I begin to get acquainted with bindings, I do not have enough background to dwell here in detail. But looking up a binding programming guide might be helpful. In general, bindings can replace many of the data source codes that you need to write on ios. When he works, he is magical. When this does not work, it looks like debugging magic. It can be a problem to see where everything went wrong.

+2
source

All Articles