Forcing a delegate method to be called - cocoa

I have a delegate method of an NSSplitView like this:
- (void)splitViewWillResizeSubviews:(NSNotification *)aNotification
{
NSLog(#"RESIZE!");
}
This method is called whenever I drag a divider, so it registered properly. I would like to call this from another object, and was thinking to use this:
[[NSNotificationCenter defaultCenter] postNotificationName:NSSplitViewWillResizeSubviewsNotification object:self];
According to the Apple docs, this is the notification that should be sent to call the delegate method. However, it does not work. Does anyone have an idea what I am doing wrong?

You can just invoke the method manually
NSSplitView * yourSplitView; //Get reference to your splitview
id yourSplitViewDelegate = [yourSplitView delegate];
[yourSplitViewDelegate splitViewWillResizeSubviews:nil];//Optionally create the NSNotification with relevant data
If you really want to go through notification center, make sure self in your question is the NSSplitView.
NSSplitView * yourSplitView; //Get reference to your splitview
[[NSNotificationCenter defaultCenter] postNotificationName:NSSplitViewWillResizeSubviewsNotification object:yourSplitView];

Turns out that I needed to manually register the delegate class for the NSSplitViewWillResizeSubviewsNotification notifications!
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(splitViewWillResizeSubviews:)
name:NSSplitViewWillResizeSubviewsNotification
object:vc];
where vc is the viewcontroller that should be sending the notifications.
This is unexpected behavior (to me), since an <NSSplitViewDelegate> is expected to register automatically for NSSplitView... notifications.

Related

Check if there are nspopover opened before creating new one

I have in my code some functionality to open a popover anytime an event happens. Problem is that if those events happen one after the other the popovers opened are overlapped.
I would like to close one popover when opening new one.
Is there any way to get from nswindow if there is an active popover?
Thanks in advance and regards
I think you need play with NSPopover's notification methods such as:
- (void)popoverDidShow:(NSNotification *)notification;
- (void)popoverWillClose:(NSNotification *)notification;
And add some logic which will hide not closed popover...
Finally I was able to figure it out by using notifications posted:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(popoverWillShow:)
name:NSPopoverWillShowNotification
object:nil];
And then in the selector I compare the objects: (popover is an NSPopover)
- (void)popoverWillShow:(NSNotification *)notification {
if (![popover isEqual:[notification object]])
[self close];
}

NSOpenglView appears in front of NSView after deminimize window

I have a NSView displayed over a NSOpenGLView. I am using the 'setWantsLayer:YES' to force the NSView to appear over the opengl context. But when I minimize the window and deminimize it again, the NSView is no more over the NSOpenGLView.
Is there a way to prevent this behaviour?
Ok, I have found a solution for this issue. Is not maybe the best, but solves the issue.
First, I have declared a notifier in my appDelegate class:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(windowDidDeminiaturize:)
name:NSWindowDidDeminiaturizeNotification object:nil];
This notifier detects the window deminimization event. Then, in the callback function, I do this:
- (void)windowDidDeminiaturize:(NSNotification *)notification
{
[view_PlaybackView setWantsLayer:NO];
[view_PlaybackView setWantsLayer:YES];
}
And the view is again shown in front of the NSOpenGLView.

Warning: Attempted to present * whose not in the window hierarchy

I have tried to deal with this issue for too long. Please give me any of your thoughts.
I am presenting a View Controller from an SKScene by sending a Local Notification. [[NSNotificationCenter defaultCenter] postNotificationName:#"closeScene" object:nil];
The notification is handled in the beginning view controller.
- (void)viewDidLoad{ [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(closeScene) name:#"closeScene" object:Nil];
[super viewDidLoad];}
Then:
-(void)closeScene {
//Remove the SKView, or present another viewController here.
constructionViewController *view = [[constructionViewController alloc] init];
[self presentViewController:view animated:YES completion:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"closeScene" object:nil];
}
And every time the notification is sent, I get the warning:
Warning: Attempt to present <constructinoViewController: 0x10ae64160> on <constructinoViewController: 0x10ab329d0> whose view is not in the window hierarchy!
Any help would more than appreciated. Thank you in advance!
When is closeScene called, exactly - before or after viewDidAppear gets called?
The error you're seeing usually happens when you try to present a view controller before the parent is actually displayed on screen. Typically this is because you've called presentViewController before the parent's viewDidAppear method has been called.
Try adding a logging statement to viewDidAppear and see whether it appears in the logs before or after your 'view is not in the window hierarchy' error. If it appears after you'll know what the problem is, and how to fix it (make sure presentViewController is only called after the parent view controller has been displayed). If it's before you probably have an altogether different problem...

How to know when a Cocoa app is about to quit?

I have a NSDocument based application. I'd like to know when the application is about to quit to validate some things. I'd hoped there might be a method such as a applicationWillQuit, but looking through the docs for both NSDocument and NSApplication I can't find anything similar.
There is a notification you can use coming from your NSApplication:
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(appWillTerminate:)
name:NSApplicationWillTerminateNotification
object:nil];
This is documented here: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/nsapplication_Class/Reference/Reference.html
By passing the object as nil your method is being called whenever an object fires the notification.
We have a delegate method in AppDelegate.swift or AppDelegate.m class. You can use it and add functionality to you application before closing it.
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}

How can I get notified when the user finishes editing a cell in an NSTableView?

I need to know when the user finishes editing a cell in an NSTableView. The table contains all of the user's calendars (obtained from the CalCalendarStore), so in order for the user's changes to be saved I need to inform the CalCalendarStore of the changes. However, I can't find anything that gets called after the user finishes their editing - I would guess that there would be a method in the table's delegate, but I only saw one that gets called when editing starts, not when editing ends.
You can achieve the same result without subclassing NSTableView by using NSNotificationCenter or using the NSControl methods. See the Apple documentation here:
http://developer.apple.com/library/mac/#qa/qa1551/_index.html
It's only a couple of lines of code and worked perfectly for me.
If you can be the delegate of the NSTableView you just need to implement the method
- (void)controlTextDidEndEditing:(NSNotification *)obj { ... }
In fact, NSTableView is the delegate of the NSControl elements it contains, and forwards those method calls to its delegate (There are other methods that are useful)
Otherwise, use the NSNotificationCenter:
// where you instantiate the table view
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(editingDidEnd:)
name:NSControlTextDidEndEditingNotification object:nil];
// somewhere else in the .m file
- (void)editingDidEnd:(NSNotification *)notification { ... }
// remove the observer in the dealloc
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSControlTextDidEndEditingNotification object:nil];
[super dealloc]
}
Subclass NSTableView and override textDidEndEditing: (be sure to call super's implementation).
This will only be invoked by text fields NSTextFieldCell or NSComboBoxCell (but only when changing the value by typing it, not by selecting the value from the combo's menu).
Set up observers for each item in the content array using addObserver:toObjectsAtIndexes:forKeyPath:options:context:
You will also need to set an observer for the array itself, so that you will be notified about objects that are added to or removed from the array.
For an example look at the iSpend project.
Look into the NSTableDataSource protocol. The message you are looking for is called: tableView:setObjectValue:forTableColumn:row:
Translating #Milly's answer into Swift 3:
// Setup editing completion notifications
NotificationCenter.default.addObserver(self, selector: #selector(editingDidEnd(_:)), name: NSNotification.Name.NSControlTextDidEndEditing, object: nil)
Function to handle notification:
func editingDidEnd(_ obj: Notification) {
guard let newName = (obj.object as? NSTextField)?.stringValue else {
return
}
// post editing logic goes here
}
Subclass NSArrayController and override objectDidEndEditing: (be sure to call super's implementation).
This will mostly only be invoked by text fields NSTextFieldCell or NSComboBoxCell (but only when changing the value by typing it, not by selecting the value from the combo's menu). There may be a few other cells that will invoke it, but I'm not sure which ones. If you have a custom cell then consider implementing the NSEditor and NSEditorRegistration informal protocols.

Resources