NSPersistent Document but probably a Bindings Noobie Cry for Help
-
Richard Ashwell NSPersistent Document but probably a Bindings Noobie Cry for Help Dec 31 2008, 20:58Ok,
First this is cry for help. Second I suck at the cocoa dev thing so I
anticipate that I won't even describe this very well, please forgive
my lack of skill in Cocoa Dev, but here goes:
I have built a relatively complex application, but have dug a whole I
can't fix or dig out of. I built the app on the Document Core Data
model, but this isn't really a core data issue (As in I haven't gotten
that deep into that part). My app was a little different (I think) in
that I wanted a controlling Main Window to be able to select / open
documents from rather than just the New/Open mechanisms provided by
the template.
To facilitate this
1) I overrode: applicationShouldOpenUntitledFile: to return NO so
that an untitled document wouldn't pop up.
2) Then I added a Window to the MainMenu Nib and added lots of code /
bindings etc to have a table of documents to choose from etc, the idea
here is there that the application overall is really a database of
individual documents.
3) I was pretty pleased to get all of this working, and the "New/Open"
still functions for opening or creating individual documents, so I
haven't broken anything there yet.
4) The next task was based on a selection in the database table, I
wanted to programatically open a document and pre populate it from the
data.
Here I ran into the issue that the document from the template was tied
to Nib via the Overridden windowNibName: method, which made it
difficult to programmatically get the Controller Window when creating
a new document from elsewhere in the code. The solution I have here
is to comment out the windowNibName method and instead implement:
- (void)makeWindowControllers
{
NSWindowController *mainWindowController = [[NSWindowController
alloc] initWithWindowNibName:@"MyDocument" owner:self];
[mainWindowController setShouldCloseDocument:YES];
[self addWindowController:mainWindowController];
[mainWindowController showWindow:self];
}
This worked and I could instantiate an instance of my Document Class
programatically when ever I want and call method makeWindowControllers
on it and the document would come up, more importantly I could then
setup the instantiated Document to have its data loaded. This all is
working with one exception:
Problem:
One view in my document nib is an NSTextView that I have A) Subclassed
and in IB set it to the Subclass, and B) IBOutleted to the main
document class for a variable there. Note this works when creating
new/open documents from the templates (I assume because the Nib
basically instantiates an instance of the sub class, and binds it for
me via the IBOutlet). Nice and dandy, however:
When I create the new document programatically (and I think/guess this
is my issue) perhaps I am getting two instantiations of the Subclassed
NSTextView or somehow when I create programatically my document class
isn't getting bound via the IBOutlet. I am only guessing and I don't
know how to troubleshoot here, which is why I am begging for help.
More details:
At this point I removed ALL functionality from the NSTextView subclass
leaving only a single method that is passed an
NSMutableAttributedString and all the method does is set the text
storage of itself and increases the font size etc, and again this
method is called and works via the new/open document stuff, but fails
when called programatically, I have traced to see that the string
passed in both cases is occurring, but since nothing shows up in the
view when I do it programatically, my only guess is that it is some
how a different instance of the subclass?
Any and all help is appreciated, I will happily code pieces etc, but
the application is a little large to link the whole thing here, but If
someone has any ideas please please, help me trouble shoot this.
Regards,
Richard Ashwell -
On Dec 31, 2008, at 11:58, Richard Ashwell wrote:> 4) The next task was based on a selection in the database table, I
> wanted to programatically open a document and pre populate it from
> the data.
It's not clear what you mean by "programmatically open a document".
Are you invoking the same action method that the Open menu item uses?
Telling the NSDocumentController to open a document? Trying to use
[[NSDocument alloc] init...]?
It also sounds like perhaps this programmatic "open" actually has the
semantics of "new". That is, you're creating a new (untitled) document
with some data from your database, which is (eventually) going to get
saved in a file separate from your database. If so, calling this
"open" is going to confuse us.> Here I ran into the issue that the document from the template was
> tied to Nib via the Overridden windowNibName: method, which made it
> difficult to programmatically get the Controller Window when
> creating a new document from elsewhere in the code. The solution I
> have here is to comment out the windowNibName method and instead
> implement:
>
> - (void)makeWindowControllers
> {
> NSWindowController *mainWindowController = [[NSWindowController
> alloc] initWithWindowNibName:@"MyDocument" owner:self];
>
> [mainWindowController setShouldCloseDocument:YES];
> [self addWindowController:mainWindowController];
> [mainWindowController showWindow:self];
> }
It's not clear how this makes it easier to "get the Controller
Window", since the reference to the controller is local to this
method. Anyway, once the controller is created, you can always get it
as [[document windowControllers] objectAtIndex: 0].> This worked and I could instantiate an instance of my Document Class
> programatically when ever I want and call method
> makeWindowControllers on it and the document would come up, more
> importantly I could then setup the instantiated Document to have its
> data loaded.
If you really are invoking makeWindowControllers yourself, you
probably shouldn't be, since it's normally invoked automatically.
Invoking your implementation manually would create a second window
controller, which isn't what you want.
To create a new document programmatically, you should probably be
invoking [NSDocumentController openUntitledDocumentAndDisplay:error:].
Are you using something else? -
Ok some more details:
I removed the subclassing of NSTextView entirely just to eliminate
that as any sort of source for the problem so now:
In my document nib I have a regular NSTextView, bound via IBOutlet to
a variable in the document class. However still the NSTextView only
responds to updating its content when the document is instantiated via
new/open rather than when created programatically. Is there some
binding I need to do instead of IBOutlet NIB binding to get that view
to respond? Does new/open somehow alloc init the variable
differently? IE am I supposed to alloc init the NSTextView in the
Document init, or was this supposed to be handled by IB?
Puzzling on..
Richard
On Dec 31, 2008, at 1:58 PM, Richard Ashwell wrote:> Ok,
>
> First this is cry for help. Second I suck at the cocoa dev thing so
> I anticipate that I won't even describe this very well, please
> forgive my lack of skill in Cocoa Dev, but here goes:
>
> I have built a relatively complex application, but have dug a whole
> I can't fix or dig out of. I built the app on the Document Core
> Data model, but this isn't really a core data issue (As in I haven't
> gotten that deep into that part). My app was a little different (I
> think) in that I wanted a controlling Main Window to be able to
> select / open documents from rather than just the New/Open
> mechanisms provided by the template.
>
> To facilitate this
>
> 1) I overrode: applicationShouldOpenUntitledFile: to return NO so
> that an untitled document wouldn't pop up.
> 2) Then I added a Window to the MainMenu Nib and added lots of
> code / bindings etc to have a table of documents to choose from etc,
> the idea here is there that the application overall is really a
> database of individual documents.
> 3) I was pretty pleased to get all of this working, and the "New/
> Open" still functions for opening or creating individual documents,
> so I haven't broken anything there yet.
> 4) The next task was based on a selection in the database table, I
> wanted to programatically open a document and pre populate it from
> the data.
>
> Here I ran into the issue that the document from the template was
> tied to Nib via the Overridden windowNibName: method, which made it
> difficult to programmatically get the Controller Window when
> creating a new document from elsewhere in the code. The solution I
> have here is to comment out the windowNibName method and instead
> implement:
>
> - (void)makeWindowControllers
> {
> NSWindowController *mainWindowController = [[NSWindowController
> alloc] initWithWindowNibName:@"MyDocument" owner:self];
>
> [mainWindowController setShouldCloseDocument:YES];
> [self addWindowController:mainWindowController];
> [mainWindowController showWindow:self];
> }
>
> This worked and I could instantiate an instance of my Document Class
> programatically when ever I want and call method
> makeWindowControllers on it and the document would come up, more
> importantly I could then setup the instantiated Document to have its
> data loaded. This all is working with one exception:
>
> Problem:
>
> One view in my document nib is an NSTextView that I have A)
> Subclassed and in IB set it to the Subclass, and B) IBOutleted to
> the main document class for a variable there. Note this works when
> creating new/open documents from the templates (I assume because the
> Nib basically instantiates an instance of the sub class, and binds
> it for me via the IBOutlet). Nice and dandy, however:
>
> When I create the new document programatically (and I think/guess
> this is my issue) perhaps I am getting two instantiations of the
> Subclassed NSTextView or somehow when I create programatically my
> document class isn't getting bound via the IBOutlet. I am only
> guessing and I don't know how to troubleshoot here, which is why I
> am begging for help.
>
> More details:
> At this point I removed ALL functionality from the NSTextView
> subclass leaving only a single method that is passed an
> NSMutableAttributedString and all the method does is set the text
> storage of itself and increases the font size etc, and again this
> method is called and works via the new/open document stuff, but
> fails when called programatically, I have traced to see that the
> string passed in both cases is occurring, but since nothing shows up
> in the view when I do it programatically, my only guess is that it
> is some how a different instance of the subclass?
>
> Any and all help is appreciated, I will happily code pieces etc, but
> the application is a little large to link the whole thing here, but
> If someone has any ideas please please, help me trouble shoot this.
>
> Regards,
> Richard Ashwell -
Quincy thanks for your reply, ok I was going to start sending you tons
of little snipits from my code, and I had the whole email, typed but
decided to reread your comments carefully.
In a nutshell:
1) I am/was creating my document class "programatically" like: (Note
Typed in email, and in my project MyDocument is actually named
something else)
MyDocument *newDoc = [[MyDocument alloc] init];
[newDoc importData:data];
By itself this wouldn't pop up a document because of the overridden
applicationShouldOpenUntitledFile: method
So I added:
[newDoc makeWindowControllers]; and the method mentioned below.
Reading your notes carefully though it looks like I maybe should be
using openUntitledDocumentAndDisplay:error instead of the
makeWindowController thing that got my document to popup perhaps only
because I added the showWindow:self to the end of that method. And
you are probably right that I am getting two instantiations, but only
"seeing" one.
I will test your suggestion first, though It might take me a few
because the template doesn't generate a NSDocumentController, only the
NSPersistentDocument class itself so I first have to figure out how to
add the Controller to my AppController class and stuff without
breaking everything, I should be able to get
openUntitledDocumentAndDisplay:error to work (I bet it does, so here
is a pre thank you!!!),
If not I think perhaps I should first deconstruct into a separate
clean project so that I can share better examples.
Regards,
Richard
On Dec 31, 2008, at 2:42 PM, Quincey Morris wrote:> On Dec 31, 2008, at 11:58, Richard Ashwell wrote:
>
>> 4) The next task was based on a selection in the database table, I
>> wanted to programatically open a document and pre populate it from
>> the data.
>
> It's not clear what you mean by "programmatically open a document".
> Are you invoking the same action method that the Open menu item
> uses? Telling the NSDocumentController to open a document? Trying to
> use [[NSDocument alloc] init...]?
>
> It also sounds like perhaps this programmatic "open" actually has
> the semantics of "new". That is, you're creating a new (untitled)
> document with some data from your database, which is (eventually)
> going to get saved in a file separate from your database. If so,
> calling this "open" is going to confuse us.
>
>> Here I ran into the issue that the document from the template was
>> tied to Nib via the Overridden windowNibName: method, which made it
>> difficult to programmatically get the Controller Window when
>> creating a new document from elsewhere in the code. The solution I
>> have here is to comment out the windowNibName method and instead
>> implement:
>>
>> - (void)makeWindowControllers
>> {
>> NSWindowController *mainWindowController = [[NSWindowController
>> alloc] initWithWindowNibName:@"MyDocument" owner:self];
>>
>> [mainWindowController setShouldCloseDocument:YES];
>> [self addWindowController:mainWindowController];
>> [mainWindowController showWindow:self];
>> }
>
> It's not clear how this makes it easier to "get the Controller
> Window", since the reference to the controller is local to this
> method. Anyway, once the controller is created, you can always get
> it as [[document windowControllers] objectAtIndex: 0].
>
>> This worked and I could instantiate an instance of my Document
>> Class programatically when ever I want and call method
>> makeWindowControllers on it and the document would come up, more
>> importantly I could then setup the instantiated Document to have
>> its data loaded.
>
> If you really are invoking makeWindowControllers yourself, you
> probably shouldn't be, since it's normally invoked automatically.
> Invoking your implementation manually would create a second window
> controller, which isn't what you want.
>
> To create a new document programmatically, you should probably be
> invoking [NSDocumentController
> openUntitledDocumentAndDisplay:error:]. Are you using something else? -
More details:
Ok I, I think I have a lot to learn before I can call
[NSDocumentController openUntitledDocumentAndDisplay:error], in the
mean time I pulled out the extra call to makeWindowControllers and
traced, When the document gets create via the New menu item the
makeWindowControllers gets called automatically like you described,
but just instantiating the document with MyDocument *newDoc =
[[MyDocument alloc] init]; doesn't call makeWindowControllers.
Perhaps that is what the NSDocumentController is supposed to do for me?
Richard
On Dec 31, 2008, at 3:49 PM, Richard Ashwell wrote:> Quincy thanks for your reply, ok I was going to start sending you
> tons of little snipits from my code, and I had the whole email,
> typed but decided to reread your comments carefully.
>
> In a nutshell:
>
> 1) I am/was creating my document class "programatically" like:
> (Note Typed in email, and in my project MyDocument is actually named
> something else)
>
> MyDocument *newDoc = [[MyDocument alloc] init];
> [newDoc importData:data];
>
> By itself this wouldn't pop up a document because of the overridden
> applicationShouldOpenUntitledFile: method
>
> So I added:
>
> [newDoc makeWindowControllers]; and the method mentioned below.
>
> Reading your notes carefully though it looks like I maybe should be
> using openUntitledDocumentAndDisplay:error instead of the
> makeWindowController thing that got my document to popup perhaps
> only because I added the showWindow:self to the end of that method.
> And you are probably right that I am getting two instantiations, but
> only "seeing" one.
>
> I will test your suggestion first, though It might take me a few
> because the template doesn't generate a NSDocumentController, only
> the NSPersistentDocument class itself so I first have to figure out
> how to add the Controller to my AppController class and stuff
> without breaking everything, I should be able to get
> openUntitledDocumentAndDisplay:error to work (I bet it does, so here
> is a pre thank you!!!),
>
> If not I think perhaps I should first deconstruct into a separate
> clean project so that I can share better examples.
>
> Regards,
> Richard
>
>
>
>
>
>
> On Dec 31, 2008, at 2:42 PM, Quincey Morris wrote:
>
>> On Dec 31, 2008, at 11:58, Richard Ashwell wrote:
>>
>>> 4) The next task was based on a selection in the database table, I
>>> wanted to programatically open a document and pre populate it from
>>> the data.
>>
>> It's not clear what you mean by "programmatically open a document".
>> Are you invoking the same action method that the Open menu item
>> uses? Telling the NSDocumentController to open a document? Trying
>> to use [[NSDocument alloc] init...]?
>>
>> It also sounds like perhaps this programmatic "open" actually has
>> the semantics of "new". That is, you're creating a new (untitled)
>> document with some data from your database, which is (eventually)
>> going to get saved in a file separate from your database. If so,
>> calling this "open" is going to confuse us.
>>
>>> Here I ran into the issue that the document from the template was
>>> tied to Nib via the Overridden windowNibName: method, which made
>>> it difficult to programmatically get the Controller Window when
>>> creating a new document from elsewhere in the code. The solution
>>> I have here is to comment out the windowNibName method and instead
>>> implement:
>>>
>>> - (void)makeWindowControllers
>>> {
>>> NSWindowController *mainWindowController = [[NSWindowController
>>> alloc] initWithWindowNibName:@"MyDocument" owner:self];
>>>
>>> [mainWindowController setShouldCloseDocument:YES];
>>> [self addWindowController:mainWindowController];
>>> [mainWindowController showWindow:self];
>>> }
>>
>> It's not clear how this makes it easier to "get the Controller
>> Window", since the reference to the controller is local to this
>> method. Anyway, once the controller is created, you can always get
>> it as [[document windowControllers] objectAtIndex: 0].
>>
>>> This worked and I could instantiate an instance of my Document
>>> Class programatically when ever I want and call method
>>> makeWindowControllers on it and the document would come up, more
>>> importantly I could then setup the instantiated Document to have
>>> its data loaded.
>>
>> If you really are invoking makeWindowControllers yourself, you
>> probably shouldn't be, since it's normally invoked automatically.
>> Invoking your implementation manually would create a second window
>> controller, which isn't what you want.
>>
>> To create a new document programmatically, you should probably be
>> invoking [NSDocumentController
>> openUntitledDocumentAndDisplay:error:]. Are you using something else? -
On Dec 31, 2008, at 13:49, Richard Ashwell wrote:> 1) I am/was creating my document class "programatically" like:
> (Note Typed in email, and in my project MyDocument is actually named
> something else)
>
> MyDocument *newDoc = [[MyDocument alloc] init];
> [newDoc importData:data];
Not so good. When creating or opening a document, there's more to do
than just creating the NSDocument instance.
openUntitledDocumentAndDisplay:error: is the way to go.> By itself this wouldn't pop up a document because of the overridden
> applicationShouldOpenUntitledFile: method
No, that had nothing to do with it. By creating the NSDocument
instance directly, you simply weren't getting the document window shown.> So I added:
>
> [newDoc makeWindowControllers]; and the method mentioned below.
>
> Reading your notes carefully though it looks like I maybe should be
> using openUntitledDocumentAndDisplay:error instead of the
> makeWindowController thing that got my document to popup perhaps
> only because I added the showWindow:self to the end of that method.
Exactly.> And you are probably right that I am getting two instantiations, but
> only "seeing" one.
>
> I will test your suggestion first, though It might take me a few
> because the template doesn't generate a NSDocumentController, only
> the NSPersistentDocument class itself so I first have to figure out
> how to add the Controller to my AppController class and stuff
> without breaking everything, I should be able to get
> openUntitledDocumentAndDisplay:error to work (I bet it does, so here
> is a pre thank you!!!),
No need to stress! NSDocumentController is a singleton object that
every AppKit application gets for free. So instead of:
MyDocument *newDoc = [[MyDocument alloc] init];
just write this:
NSError *error;
MyDocument *newDoc = [[NSDocumentController sharedDocumentController]
openUntitledDocumentAndDisplay: YES error: &error];
if (!newDoc)
... // report the problem described in 'error'> ... I pulled out the extra call to makeWindowControllers and traced,
> When the document gets create via the New menu item the
> makeWindowControllers gets called automatically like you described,
> but just instantiating the document with MyDocument *newDoc =
> [[MyDocument alloc] init]; doesn't call makeWindowControllers.
> Perhaps that is what the NSDocumentController is supposed to do for
> me?
Yup. It also causes the document to appear on the Window menu for you,
and populates the Open Recent menu. -
If you're looking at the same split view as I am (main window), it
appears that they're just using a horizontal split view as the right
subview in a vertical split view. There doesn't seem to be a resize
handle that adjusts both directions.
On 31-Dec-08, at 5:27 PM, Quincey Morris wrote:> On Dec 31, 2008, at 13:49, Richard Ashwell wrote:
>
>> 1) I am/was creating my document class "programatically" like:
>> (Note Typed in email, and in my project MyDocument is actually
>> named something else)
>>
>> MyDocument *newDoc = [[MyDocument alloc] init];
>> [newDoc importData:data];
>
> Not so good. When creating or opening a document, there's more to do
> than just creating the NSDocument instance.
> openUntitledDocumentAndDisplay:error: is the way to go.
>
>> By itself this wouldn't pop up a document because of the overridden
>> applicationShouldOpenUntitledFile: method
>
> No, that had nothing to do with it. By creating the NSDocument
> instance directly, you simply weren't getting the document window
> shown.
>
>> So I added:
>>
>> [newDoc makeWindowControllers]; and the method mentioned below.
>>
>> Reading your notes carefully though it looks like I maybe should be
>> using openUntitledDocumentAndDisplay:error instead of the
>> makeWindowController thing that got my document to popup perhaps
>> only because I added the showWindow:self to the end of that method.
>
> Exactly.
>
>> And you are probably right that I am getting two instantiations,
>> but only "seeing" one.
>>
>> I will test your suggestion first, though It might take me a few
>> because the template doesn't generate a NSDocumentController, only
>> the NSPersistentDocument class itself so I first have to figure out
>> how to add the Controller to my AppController class and stuff
>> without breaking everything, I should be able to get
>> openUntitledDocumentAndDisplay:error to work (I bet it does, so
>> here is a pre thank you!!!),
>
> No need to stress! NSDocumentController is a singleton object that
> every AppKit application gets for free. So instead of:
>
> MyDocument *newDoc = [[MyDocument alloc] init];
>
> just write this:
>
> NSError *error;
> MyDocument *newDoc = [[NSDocumentController
> sharedDocumentController] openUntitledDocumentAndDisplay: YES error:
> &error];
> if (!newDoc)
> ... // report the problem described in 'error'
>
>> ... I pulled out the extra call to makeWindowControllers and
>> traced, When the document gets create via the New menu item the
>> makeWindowControllers gets called automatically like you described,
>> but just instantiating the document with MyDocument *newDoc =
>> [[MyDocument alloc] init]; doesn't call makeWindowControllers.
>> Perhaps that is what the NSDocumentController is supposed to do for
>> me?
>
> Yup. It also causes the document to appear on the Window menu for
> you, and populates the Open Recent menu. -
Sweet!!!! Sweet!!! Magic!!! the :> NSError *error;
> MyDocument *newDoc = [[NSDocumentController
> sharedDocumentController] openUntitledDocumentAndDisplay: YES error:
> &error];
was the key, thank you so much Quincey!!! This both instantiates and
lets me controll what is created!
Yeah!! What a great end to the year, i'll actually go into the new
year with working code :)
Regards,
Richard
On Dec 31, 2008, at 4:27 PM, Quincey Morris wrote:> On Dec 31, 2008, at 13:49, Richard Ashwell wrote:
>
>> 1) I am/was creating my document class "programatically" like:
>> (Note Typed in email, and in my project MyDocument is actually
>> named something else)
>>
>> MyDocument *newDoc = [[MyDocument alloc] init];
>> [newDoc importData:data];
>
> Not so good. When creating or opening a document, there's more to do
> than just creating the NSDocument instance.
> openUntitledDocumentAndDisplay:error: is the way to go.
>
>> By itself this wouldn't pop up a document because of the overridden
>> applicationShouldOpenUntitledFile: method
>
> No, that had nothing to do with it. By creating the NSDocument
> instance directly, you simply weren't getting the document window
> shown.
>
>> So I added:
>>
>> [newDoc makeWindowControllers]; and the method mentioned below.
>>
>> Reading your notes carefully though it looks like I maybe should be
>> using openUntitledDocumentAndDisplay:error instead of the
>> makeWindowController thing that got my document to popup perhaps
>> only because I added the showWindow:self to the end of that method.
>
> Exactly.
>
>> And you are probably right that I am getting two instantiations,
>> but only "seeing" one.
>>
>> I will test your suggestion first, though It might take me a few
>> because the template doesn't generate a NSDocumentController, only
>> the NSPersistentDocument class itself so I first have to figure out
>> how to add the Controller to my AppController class and stuff
>> without breaking everything, I should be able to get
>> openUntitledDocumentAndDisplay:error to work (I bet it does, so
>> here is a pre thank you!!!),
>
> No need to stress! NSDocumentController is a singleton object that
> every AppKit application gets for free. So instead of:
>
> MyDocument *newDoc = [[MyDocument alloc] init];
>
> just write this:
>
> NSError *error;
> MyDocument *newDoc = [[NSDocumentController
> sharedDocumentController] openUntitledDocumentAndDisplay: YES error:
> &error];
> if (!newDoc)
> ... // report the problem described in 'error'
>
>> ... I pulled out the extra call to makeWindowControllers and
>> traced, When the document gets create via the New menu item the
>> makeWindowControllers gets called automatically like you described,
>> but just instantiating the document with MyDocument *newDoc =
>> [[MyDocument alloc] init]; doesn't call makeWindowControllers.
>> Perhaps that is what the NSDocumentController is supposed to do for
>> me?
>
> Yup. It also causes the document to appear on the Window menu for
> you, and populates the Open Recent menu.


