NSTableCellView / NSTextField did end editing notification -- without text change? - objective-c

How can I get a notification when the textfield has ended editing (enter key pressed, clicked outside the text field, clicked inside the same column, but outside the text field, etc)
I checked
- (void)textDidEndEditing:(NSNotification *)aNotification
{
//some code here
}
but this notification is only called when the text has actually changed. I need to be notified even if no text changes have been made.
Edit: Some sort of notification that the first responder has changed would work as well.
Any ideas?

Observe the firstResponder state of the window …
…
[theWindow addObserver:self forKeyPath:#"firstResponder" options:NSKeyValueObservingOptionOld context:NULL];
…
… and read the field editor's delegate:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"firstResponder"])
{
NSResponder *oldResponder =change[NSKeyValueChangeOldKey];
if ([oldResponder isKindOfClass:[NSTextView class]])
{
NSTextView *editor = (NSTextView*)oldResponder;
NSTextField *textField = (NSTextField*)editor.delegate;
NSLog(#"This text field lost the focus: %#", textField.identifier);
}
}
}

Related

Detect when SKNode is added as child

I have a Polygon class that is used as data container, but for debugging purposes I want to draw polygons by adding them as children to an SKNode. When I do this I want the Polygon object to add border sprite children to itself. Because of performance reasons I only want to add those sprites when the Polygon has been added as child to another node.
Is there any way that the Polygon object itself can detect that it has been added to the scene, or do I need to tell it by making an extra createSprites call after it has been added to the scene?
I guess I can poll the parent attribute, but I'm looking for something event driven.
In Swift, you can define property observers for the properties of the class yours inherits from.
You could observe changes in the parent property of your custom SKNode subclass, like this:
class MyNode : SKNode {
override var parent: SKNode? {
didSet {
// parent node changed; do something
}
}
}
EDIT: Like I mentioned in the comments, in Objective-C (where you can not use property observers) you can instead use Key-Value Observing (KVO), and observe changes in SKNode's parent property:
Actual code:
- (instancetype) init
{
if (self = [super init]){
// REGISTER for KVO
[self addObserver:self
forKeyPath:#"parent"
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
- (void) dealloc
{
// UNREGISTER from KVO
[self removeObserver:self forKeyPath:#"parent" context:NULL];
}
// Method that is called when any keyPath you subscribed to is modified
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
// (Check keypath in case you are observing several different
// properties. Otherwise, remove if statement:)
if ([keyPath isEqualToString:#"parent"]) {
// parent node changed; do something
}
}
In Objective-C you could do it like this:
[node addObserver:self forKeyPath:#"parent" options:NSKeyValueChangeOldKey context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
//logic here
}
Or in this case simply subclass SKNode and override setParent:

Key-value observing for QLPreviewController.currentPreviewItemIndex

I have an object that need to be notified when a QLPreviewController changes the shown document. QLPreviewController have the property currentPreviewItemIndex that is updated when the document change. I've added my object as observer for currentPreviewItemIndex and it receives the notification when in my code is changed the property, so far so good.
The problem is that the user can change the shown document swiping in the screen and I've found out that in this case the notification isn't generated.
Any solution to receive the notification also in this case? I suppose that the notification is generated when is called the setter of the property currentPreviewItemIndex and probably when the user swipe the property is changed internally in the object QLPreviewController.
Another solution may be to disable the horizontal swipe in QLPreviewController but preserving the vertical swipe (there are the arrows buttons to change the shown document). How do you do that?
Thanks in advance for the help.
Giannandrea
make a category on the QLPreviewController and swizzle the appropriate method and either add the willChange/didChange for KVO ;)
seriously though:
I tried KVO and it didnt work for me either.. 1) id file a bug with apple for that saying you need this
BUT as a workaround
(id )previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index {
this is called ok and everytime we swipe so I would 'hack' this to FIRE your own correct KVO. something like
static NSInteger oldIndex = -1; //reset when the panel is hidden or shown
int newIndex = qlController.displayedIndex;
if(oldIndex != newIndex) {
oldIndex = newIndex;
[qlController willChangeValueForKey:#"displayedIndex"];
[qlController didChangeValueForKey:#"displayedIndex"];
}
I wrote it inline here so there are bound to be typos and mistakes but I think the general approach could work.
//1. Declare a static context:
static void *changePageContext = &changePageContext;
//2. In viewDidLoad add self as observer for currentPreviewItemIndex property of a strong ref to your QLPreviewController:
[self.previewController addObserver:self forKeyPath:#"currentPreviewItemIndex" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:changePageContext];
//3. Implement the observer method:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (context == changePageContext)
{
NSLog(#"newValue:%ld",(long)self.previewController.currentPreviewItemIndex);
}
else
{
// Any unrecognized context must belong to super
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
//4. Remove the observer in viewWillDisappear:
-(void)viewWillDisappear:(BOOL)animated
{
if (![[self.navigationController viewControllers] containsObject: self])
{
[self.previewController removeObserver:self forKeyPath:#"currentPreviewItemIndex"];
}
}

Notification to know nstableview column start resizing

I am working on a MAC application in which I need to get an event that user start resizing column of nstableview. I know there is a notification columnDidResize. But it get called when we ended resizing columns.
Solved the same problem using KVO notifications.
Set your table delegate as an observer for NSTableColumn width:
[column addObserver:self forKeyPath:#"width" options:0 context:nil];
Options argument could be adjusted to get the notification before an actual change takes place.
Then get notified when the width changes:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSInteger resizedColumn = tableView.headerView.resizedColumn;
if (resizedColumn != -1)
{
if (object == column &&
object == [tableView.tableColumns objectAtIndex:resizedColumn])
{
// User is resizing column
}
}
}
Using KVO, as indicated by #pointum works. If you get into troubles related to unregistering the observer, you can do what I actually ended up doing:
Subclass NSTableColumn and override the setter for the Width parameter and send a notification from there.

iOS: addObserver and superview query

I have a uiview named subview1. I add this as subview to a couple of other views depending on certain situations. Now I have the following code
[subView1 addObserver:self forKeyPath:#"superview" options:NSKeyValueObservingOptionNew context:nil];
My problem is the obserValueForKeypath function is never called
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if (self.subView1 == (UIView*)object) {
if ([keyPath isEqualToString:#"superview"]) {
NSLog(#"superview changed %#",change);
}
}
}
Am i doing something wrong here.
Just check that if it is going into first if block , the problem might be there.Also check if you have declared property for variable for which you are setting the observer if it is in different class.

autohide only horizontal scroller in NSScrollView

I have an NSTableView, created from IB, that I want to only autohide the horizontal scroller on. The main reason I want to do this is because it seems the NSTableView corverView only get's displayed if there is a vertical scroller.
I can't find any method to do this with the base class. So I tried subclassing NSScrollView and observing the hidden key on the horizontal scroller (code below). This works; however, the view tries to reset the current visible options every time the user resizes the window. This makes my implementation somewhat expensive; and it seems inelegant. Any better ideas about how to do this?
Thanks in advance!
Current implementation:
#interface PVScrollView : NSScrollView {
BOOL autohidesHorizontalScroller;
}
#property(assign) BOOL autohidesHorizontalScroller;
- (void) viewResized:(NSNotification*)notification;
#end
#implementation PVScrollView
#synthesize autohidesHorizontalScroller;
- (void) setAutohidesHorizontalScroller:(BOOL)val
{
autohidesHorizontalScroller = val;
[self setAutohidesScrollers:NO];
[[self horizontalScroller] addObserver:self
forKeyPath:#"hidden"
options:0
context:nil];
}
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (!([self documentVisibleRect].size.width < [[self documentView] frame].size.width) )
{
// remove observer
[[self horizontalScroller] removeObserver:self
forKeyPath:#"hidden"];
[[self horizontalScroller] setHidden:YES];
//[[self horizontalScroller] setNeedsDisplay:YES];
// add it back
[[self horizontalScroller] addObserver:self
forKeyPath:#"hidden"
options:0
context:nil];
}
}
#end
Give this a shot in your NSScrollView subclass:
- (void)setFrameSize:(NSSize)newSize;
{
NSSize minFrameSize = [NSScrollView frameSizeForContentSize:[self contentSize] hasHorizontalScroller:NO hasVerticalScroller:YES borderType:[self borderType]];
BOOL wantScroller = minFrameSize.width > newSize.width;
[self setHasHorizontalScroller:wantScroller];
[super setFrameSize: newSize];
}
You'll need to check "Show Vertical Scroller" and uncheck "Automatically Hide Scrollers" for it to work; I didn't bother making it robust to changes in IB. Also, you'll need to do the same thing when the window is first displayed (in the NSScrollView constructor).
I compared CPU usage with and without this change; it seems to vary at most 1% (19%→20%) in my test application.