How to observe the value of an NSTextField - key-value-observing

This might seem straightforward, but the following code does not work, because the "observeValueForKeyPath" function is never called, although I keep changing the text in the NSTextfield:
- (void)awakeFromNib
{
[myNSTextField addObserver:self forKeyPath:#"value" options:NSKeyValueObservingOptionOld context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"observeValueForKeyPath");
}
The log message #"observeValueForKeyPath" is never printed. I tried observing the key #"stringValue", but this does not work neither...
Am I missing something ??

[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldDIdChange:)
name:NSControlTextDidChangeNotification
object:self.textField];

[[NSNotificationCenter defaultCenter] addObserverForName:NSTextViewDidChangeSelectionNotification
object:self.textView
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note){
NSLog(#"Text: %#", self.textView.textStorage.string);
}];

Related

NSTask Blocking My UI when NSTask encounter large data

I want to update my nstextview with the data generated during nstask execution(Ipa Generation). But when i run my code to execute nstask, in the middle my nstask blocks my ui but the task continues to execute. At last when nstask terminates my ui starts working properly.
This is my code where i am running my nstask:
dispatch_queue_t taskQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
NSTask *task=[[NSTask alloc]init];
dispatch_async(taskQueue, ^{
#try {
[task setArguments:arguments];
[task setLaunchPath: launchPath];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(taskCompletion:) name: NSTaskDidTerminateNotification object:task];
// Output Handling
NSPipe *outputPipe = [[NSPipe alloc] init];
outputFileHandle = [[NSFileHandle alloc]init];
[task setStandardOutput:outputPipe];
outputFileHandle=[outputPipe fileHandleForReading];
[outputFileHandle waitForDataInBackgroundAndNotify];
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification object:outputFileHandle queue:nil usingBlock:^(NSNotification *notification){
NSData *output = [outputFileHandle availableData];
NSString *outStr = [[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(taskCompletion:) name: NSTaskDidTerminateNotification object:task];
NSLog(#"%#",outStr);
NSString *textViewData =[self.outputView string];
self.outputView.string = [textViewData stringByAppendingString:[NSString stringWithFormat:#"\n%#", outStr]];
// Scroll to end of outputText field
NSRange range;
range = NSMakeRange([self.outputView.string length], 0);
[self.outputView scrollRangeToVisible:range];
});
[outputFileHandle waitForDataInBackgroundAndNotify];
}];
[task launch];
[task waitUntilExit];
}
#catch (NSException *exception) {
NSLog(#"Problem Running Task: %#", [exception description]);
}
#finally {
NSLog(#"i m in finally xbuild");
}
});
I really stuck in that .Your suggestions will be helpful for me.

UIManagedDocument - How to deal with UIDocumentStateSavingError?

I am working on my first iCloud App. After working for a while the app cannot access a UIManagedDocument any more due to an "UIDocumentStateSavingError". Is there any way to actually find out what error occurred?
This is my code to create the UIManagedDocument:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
iCloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (iCloudURL == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
[self iCloudNotAvailable];
});
return;
}
iCloudDocumentsURL = [iCloudURL URLByAppendingPathComponent:#"Documents"];
iCloudCoreDataLogFilesURL = [iCloudURL URLByAppendingPathComponent:#"TransactionLogs"];
NSURL *url = [iCloudDocumentsURL URLByAppendingPathComponent:#"CloudDatabase"];
iCloudDatabaseDocument = [[UIManagedDocument alloc] initWithFileURL:url];
NSMutableDictionary *options = [NSMutableDictionary dictionary];
NSString *name = [iCloudDatabaseDocument.fileURL lastPathComponent];
[options setObject:name forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:iCloudCoreDataLogFilesURL forKey:NSPersistentStoreUbiquitousContentURLKey];
iCloudDatabaseDocument.persistentStoreOptions = options;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(documentContentsChanged:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:iCloudDatabaseDocument.managedObjectContext.persistentStoreCoordinator];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(documentStateChanged:) name:UIDocumentStateChangedNotification object:iCloudDatabaseDocument];
if ([[NSFileManager defaultManager] fileExistsAtPath:[iCloudDatabaseDocument.fileURL path]]) {
// This is true, the document exists.
if (iCloudDatabaseDocument.documentState == UIDocumentStateClosed) {
[iCloudDatabaseDocument openWithCompletionHandler:^(BOOL success) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
[self documentConnectionIsReady];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self connectionError:iCloudConnectionErrorFailedToOpen];
});
}
}];
} else if (iCloudDatabaseDocument.documentState == UIDocumentStateNormal) {
...
}
} else {
...
}
});
The Document already exists and thus openWithCompletionHandler: is called on the document. This fails and the UIDocumentStateChangedNotification is fired which shows a document states of 5:
UIDocumentStateClosed and
UIDocumentStateSavingError
After this the completion block gets called. What is correct way to proceed from here? Is there any way to find out what went wrong and what kind of error occurred?
I tried to re-open the document in the completion block but the result is the same.
I guess I could solve the problem by just deleting the file and recreate it. But this is obviously not an option once the app will be out in the store. I would like to know what is going wrong and give the user an appropriator way to handle the problem.
I already checked other questions here handling the UIDocumentStateSavingError (there a not a lot of them) but the seem not to be applicable for the problem here.
Any idea how I can find out what the problem is? I cannot belive that the API tells you "Something went wrong during saving but I will not tell you what!"
You can query the documentState in the completion handler. Unfortunately, if you want the exact error, the only way to get it is to subclass and override handleError:userInteractionPermitted:
Maybe something like this would help (typed freehand without compiler)...
#interface MyManagedDocument : UIManagedDocument
- (void)handleError:(NSError *)error
userInteractionPermitted:(BOOL)userInteractionPermitted;
#property (nonatomic, strong) NSError *lastError;
#end
#implementation MyManagedDocument
#synthesize lastError = _lastError;
- (void)handleError:(NSError *)error
userInteractionPermitted:(BOOL)userInteractionPermitted
{
self.lastError = error;
[super handleError:error
userInteractionPermitted:userInteractionPermitted];
}
#end
Then in you can create it like this...
iCloudDatabaseDocument = [[UIManagedDocument alloc] initWithFileURL:url];
and use it in the completion handler like this...
[iCloudDatabaseDocument openWithCompletionHandler:^(BOOL success) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
[self documentConnectionIsReady];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self connectionError:iCloudConnectionErrorFailedToOpen
withError:iCloudDatabaseDocument.lastError];
});
}
}];
Based on #JodyHagins excellent snippet, I have made a UIDocument subclass.
#interface SSDocument : UIDocument
- (void)openWithSuccess:(void (^)())successBlock
failureBlock:(void (^)(NSError *error))failureBlock;
#end
#interface SSDocument ()
#property (nonatomic, strong) NSError *lastError;
#end
#implementation SSDocument
- (void)handleError:(NSError *)error userInteractionPermitted:(BOOL)userInteractionPermitted {
self.lastError = error;
[super handleError:error userInteractionPermitted:userInteractionPermitted];
}
- (void)clearLastError {
self.lastError = nil;
}
- (void)openWithSuccess:(void (^)())successBlock failureBlock:(void (^)(NSError *error))failureBlock {
NSParameterAssert(successBlock);
NSParameterAssert(failureBlock);
[self clearLastError];
[self openWithCompletionHandler:^(BOOL success) {
if (success) {
successBlock();
} else {
NSError *error = self.lastError;
[self clearLastError];
failureBlock(error);
}
}];
}
#end

Core Data and multithreading (and Bindings to make it more fun)

I have got this background thread that does a few things with core data objects. I get the context as follows:
- (id)_managedObjectContextForThread;
{
NSManagedObjectContext * newContext = [[[NSThread currentThread] threadDictionary] valueForKey:#"managedObjectContext"];
if(newContext) return newContext;
newContext = [[NSManagedObjectContext alloc] init];
[newContext setPersistentStoreCoordinator:[[[NSApplication sharedApplication] delegate] persistentStoreCoordinator]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_mergeChangesFromManagedObjectContext:)
name:NSManagedObjectContextDidSaveNotification
object:newContext];
[[[NSThread currentThread] threadDictionary] setValue:newContext forKey:#"managedObjectContext"];
return newContext;
}
then I fetch some objects, modify them and save the context:
- (void) saveContext:(NSManagedObjectContext*)context {
NSError *error = nil;
if (![context save:&error]) {
[[NSApplication sharedApplication] presentError:error];
}
}
- (void)_mergeChangesFromManagedObjectContext:(NSNotification*)notification;
{
[[[[NSApplication sharedApplication] delegate] managedObjectContext] performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
.. later I remove the observer. This works for the main part. But some properties don't get updated when they get merged back. The properties that were nil before get updated. The ones that had a value stay the same.
I tried:
[newContext setMergePolicy:NSOverwriteMergePolicy];
... (and the other merge policies) on the main context but it did not work :P
Thank you for your help.
Note: I have bound the values to a NSTableView. I log them after the merge. The values properties that were nil seem to work fine.
How are you registering both contexts for notifications? You need to do something like this:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundMOC];
[nc addObserver:self
selector:#selector(mainContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:mainMOC];
And implement the callbacks:
// merge changes in background thread if main context changes
- (void)mainContextDidSave:(NSNotification *)notification
{
SEL selector = #selector(mergeChangesFromContextDidSaveNotification:);
[backgroundMOC performSelector:selector onThread:background_thread withObject:notification waitUntilDone:NO];
}
// merge changes in main thread if background context changes
- (void)backgroundContextDidSave:(NSNotification *)notification
{
if ([NSThread isMainThread]) {
[mainMOC mergeChangesFromContextDidSaveNotification:notification];
}
else {
[self performSelectorOnMainThread:#selector(backgroundContextDidSave:) withObject:notification waitUntilDone:NO];
}
}

NSNotificationCenter with arguments

I am implementing an Audio based application. In that I am playing two different sounds using two AVPlayers. I need to do different actions once the sounds played. For this I used NSNotifications. But my problem is I am not able to find the Notifications related to which player. My notifications code and selector code is as follows please any one suggest me what the mistake I did.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playingItemDidEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:iPodPlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playingItemDidEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:applicationPlayer ];
- (void)playingItemDidEnd:(NSNotification *)notification
{
id object= [notification object];
if(object==ipodPlayer)
{
printf("\n Notification from iPod Player ");
}
else if(object==applicationPlayer)
{
printf("\n Notification from application Player ");
}
}
Thanks in advance,
Chandra.
I need to change the code base as follows,
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playingItemDidEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[applicationPlayer currentItem] ];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playingItemDidEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[iPodPlayer currentItem]];
And selector code should be as follows,
- (void)playingItemDidEnd:(NSNotification *)notification
{
AVPlayerItem* object= [notification object];
if(object==[applicationPlayer currentItem])
{
}
else if(object==[avPlayer currentItem])
{
}
}

NSNotification Does Not Notify

I have an application that shows a lot of videos. To load and play the file, I use the following code:
- (IBAction)playVideoooo {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:
[NSURL URLWithString:#"/UnioneDiCentro2011_Live.isml/manifest(format=m3u8-aapl)"]];
switch ( [self interfaceOrientation] ) {
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
[[moviePlayerController view] setFrame:CGRectMake(0, 0, P_WIDTH, P_HEIGHT)];
break;
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
[[moviePlayerController view] setFrame:CGRectMake(0, 0, L_WIDTH, L_HEIGHT)];
break;
}
[moviePlayerController prepareToPlay];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil]; // Register that the load state changed (movie is ready)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
[[self view] addSubview:[moviePlayerController view]];
}
- (void)moviePlayerLoadStateChanged:(NSNotification*)notification {
// Unless state is unknown, start playback
if ([moviePlayerController loadState] != MPMovieLoadStateUnknown) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
[[UIApplication sharedApplication] setStatusBarOrientation:[self interfaceOrientation]
animated:YES];
[moviePlayerController play];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
}
- (void)moviePlayBackDidFinish:(NSNotification*)notification {
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
switch ( [self interfaceOrientation] ) {
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
[[moviePlayerController view] setFrame:CGRectMake(0, 0, P_WIDTH, P_HEIGHT)];
break;
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
[[moviePlayerController view] setFrame:CGRectMake(0, 0, L_WIDTH, L_HEIGHT)];
break;
}
if ( [moviePlayerController isFullscreen] ) {
[moviePlayerController setFullscreen:NO];
}
}
Actually the system seems to works, but I have to push the button linked to "playVideooooo" two times, to let the notification work. If I move the [moviePlayerController play]; into the IBActions the video starts correctly. How should I get the Notifications work?
Question partially solved: the matter is not in the NSNotification but in PrepareToPlay.

Resources