How do I load PersistentDocuments into the same window - cocoa

I want to open NSPersistentDocuments and load them into the same window one at a time. I'm almost there but missing some steps. Hopefully someone can help me.
I have a few saved documents on the hard drive. On launch my app opens to an untitled NSPersistentDocument and creates a separate NSWindowController. When I press the button to load file 1 off the hard drive the data appears in the fields but two things are wrong that I can see:
1) changing the data doesn't make the document dirty
2) choosing save updates the persistentstore (I know this because when I open the file again I see the changes) but I get an error: +entityForName: could not locate an NSManagedObjectModel for entity name 'Book'
Here's my code which is in the WindowController that was launched initially with the untitled document. This code isn't perfect. For example, I know I should processPendingChanges and save the current doc before I load the new one. This is test code to try to get over this hurdle.
- (IBAction)newBookTwo:(id)sender {
NSDocumentController *dc = [NSDocumentController sharedDocumentController];
NSURL *url = [NSURL fileURLWithPath:[#"~/Desktop/File 2.binary" stringByExpandingTildeInPath]];
NSError *error;
MainWindowDocument *thisDoc = [dc openDocumentWithContentsOfURL:url display:NO error:&error];
[self setDocument:thisDoc];
[self setManagedObjectContext:[thisDoc managedObjectContext]];
}
Thanks!

Assuming you have an entity called "Book" or "book" then the second problem is most likely a typo swapping case. Depending on the entity graph that may be triggering your first problem as well.

Put a break point in there and see if your model is nil.

Related

how to show a progress bar on file's icon

You know there is a mini progress bar on the file's icon when we copy a file or download file with safari and chrome.
I am wondering how to let it shown in Finder window when I'm copying or download files with my own code.
Could somebody help me?
Thanks a lot.
I got the way by query file/directory attributes myself. It's quit simple.
Get attributes of the file you want to show a progress bar on it's icon
NSDictionary *fileAttr = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
Get extend attributes
NSDictionary *extendAttr = [fileAttr objectForKey:#"NSFileExtendedAttributes"];
Create a Mutable copy of extendAttr and change some value in it
NSMutableDictionary *mutableExtendAttr = [NSMutableDictionary dictionaryWithDictionary:extendAttr];
// create data of progress value
NSData *progressData = [#"0.1" dataUsingEncoding:NSASCIIStringEncoding];
// change it
[mutableExtendAttr setObject:progressData forKey:#"com.apple.progress.fractionCompleted"];
Create a mutable copy of fileAttr
NSMutableDictionary *mutableFileAttr = [NSMutableDictionary dictionaryWithDictionary:fileAttr];
// change extend attr
[mutableFileAttr setObject:[NSDictionary dictionaryWithDictionary:mutableExtendAttr] forKey:extendAttrKey];
// change file/dir create date to the special one
// if not , progress bar will not show
[mutableFileAttr setObject:[NSDate dateWithString:#"1984-01-24 08:00:00 +0000"] forKey:NSFileCreationDate];
// set attribute to file
[[NSFileManager defaultManager] setAttributes:mutableFileAttr ofItemAtPath:filePath error:&error];
Now you'll find it the progress bar showed up.
One starting point would be to use a NSProgressIndicator, then [progressIndicator setIndeterminate:NO];. Your updates/background operation should then not block the main thread. NSProgressIndicator is a bit different for an AppKit widget -- it permits some updates from secondary threads. Of course, updates to the 'current progress' may also be enqueued upon the main thread's run loop.

Save ManagedObjectContext in Document based App?

I have problems saving my MOC in a PersistentDocument.
I insert a new MO in my MOC and do a save (for e.g.)
NSManagedObject *person=[[NSManagedObject alloc] initWithEntity:[NSEntityDescription entityForName:#"Person" inManagedObjectContext:self.managedObjectContext] insertIntoManagedObjectContext:self.managedObjectContext];
if(person)
{
[person setValue:#"test" forKey:#"name"];
NSError *error;
[self.managedObjectContext save:&error];
}
I must save the MO at this point because I need a NOT temporary objectID of it for my background thread to do some calculation on the entity.
But when I save the MOC in this way, I get a requester
The document “Untitled.binary” could not be saved. The file has been changed by another application.
Click Save Anyway to keep your changes and save the changes made by the other application as a version, or click Revert to keep the changes from the other application and save your changes as a version.
the next time the Document is trying to save its content. I can not save the document instead, because it is possible that it is still an untitled document and a call of [document save] would open the save requester which ist not very comfortable for the user.
Is there any solution?
Thanks
Claus
Instead of trying to save you MOC, try
[self.managedObjectContext processPendingChanges];
I had the same issue on an App I'm working on where changes just made would not be there to reference. I tried saving the MOC and keep getting the same message. Calling the processPendingChanges worked for me.

Select row in UITableView

I'm pretty new to xcode development and I cannot figure out how to select a row and give label/action I want. I'm using something i found on the internet, sample code with a customized uitableview with 3 rows:
opLabel1.text = [NSString stringWithFormat:#"HeliosMedica",[indexPath section]];
bottomLabel1.text = [NSString stringWithFormat:#"Centrumedical.", [indexPath section]];
I want each row to have different content and also give them a hyperlink. Can anyone tell me where how I can do that.
Appreciate very much your help.
Read the Apple Documents for UITableViewDelegate Protocol Reference. You want to understand some of the basic methods there, including tableView:didSelectRowAtIndexPath:
You probably already have implemented some of the UITableView class methods, in order to get a table to just show up on our screen, including numberOfSections, numberOfSections:and insertRowsAtIndexPaths:withRowAnimation:. These are called by the tableview object and your code executes to meet the tableView's needs.
Here is some sample code you can start with to open a url when the tableView:didSelectRowAtIndexPath:method is executed.
myURL = [arrayOfURLs objectAtIndex:indexPath.row];
[[UIApplication sharedApplication] openURL:myURL];
You will have to initialize the array, first.

Bring all NSDocument windows to front when opened

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 do I set the default selection for NSTreeController at startup?

The Background
I've built a source list (similar to iTunes et al.) in my Cocoa app.
I've got an NSOutlineView, with Value
column bound to arrangedObjects.name
key path of an NSTreeController.
The NSTreeController accesses
JGSourceListNode entities in a Core
Data store.
I have three subclasses of
JGSourceListNode - JGProjectNode,
JGGroupNode and JGFolderNode.
I have selectedIndexPaths on NSTreeController bound to an NSArray called selectedIndexPaths in my App Delegate.
On startup, I search for group nodes and if they're not found in the core data store I create them:
if ([allGroupNodes count] == 0) {
JGGroupNode *rootTrainingNode = [JGGroupNode insertInManagedObjectContext:context];
[rootTrainingNode setNodeName:#"TRAIN"];
JGProjectNode *childUntrainedNode = [JGProjectNode insertInManagedObjectContext:context];
[childUntrainedNode setParent:rootTrainingNode];
[childUntrainedNode setNodeName:#"Untrained"];
JGGroupNode *rootBrowsingNode = [JGGroupNode insertInManagedObjectContext:context];
[rootBrowsingNode setNodeName:#"BROWSE"];
JGFolderNode *childFolder = [JGFolderNode insertInManagedObjectContext:context];
[childFolder setNodeName:#"Folder"];
[childFolder setParent:rootBrowsingNode];
[context save:nil];
}
What I Want
When I start the app, I want both top level groups to be expanded and "Untrained" to be highlighted as shown:
My Window http://synapticmishap.co.uk/Window.jpeg
The Problem
I put the following code in the applicationDidFinishLaunching: method of the app delegate:
[sourceListOutlineView expandItem:[sourceListOutlineView itemAtRow:0]];
[sourceListOutlineView expandItem:[sourceListOutlineView itemAtRow:2]];
NSIndexPath *rootIndexPath = [NSIndexPath indexPathWithIndex:0];
NSIndexPath *childIndexPath = [rootIndexPath indexPathByAddingIndex:0];
[self setSelectedIndexPaths:[NSArray arrayWithObject:childIndexPath]];
but the outline view seems to not have been prepared yet, so this code does nothing.
Ideally, eventually I want to save the last selection the user had made and restore this on a restart.
The Question
I'm sure it's possible using some crazy KVO to observe when the NSTreeController or NSOutlineView gets populated then expand the items and change the selection, but that feels clumsy and too much like a work around.
How would I do this elegantly?
Elegantly? This isn't elegant but it's how I'm doing it. I just do it manually. At app quit I write this value to user defaults:
lastSelectedRow = [outlineView selectedRow]
Then at app launch I run this in app did finish launching:
[self performSelector:#selector(selectLastNoteOrCreateDefaultNote) withObject:nil afterDelay:1];
Notice I just use a delay because I noticed the same as you that "the outline view seems to not have been prepared yet". Then in that selector I use this.
[outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:lastSelectedRow] byExtendingSelection:NO];
It works but better (more elegant) solutions are welcome from me too.

Resources