NSOutlineView outlineViewSelectionDidChange - cocoa

my NSOutlineView outlineViewSelectionDidChange method will not be called.
I set set the NSOutlineViews delegate to the class where the other methods such as
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
exist. But outlineViewSelectionDidChange will not be called on selecting an item.
Does anybody has an idea?

This notification is a bit odd, in that it is not automatically forwarded to delegates. Try adding an explicit registration to your initialization code, like this example:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController;
{
[super windowControllerDidLoadNib:aController];
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:#selector(outlineViewSelectionDidChange:)
name:#"NSOutlineViewSelectionDidChangeNotification"
object:outlineView];
}

Okay,
meanwhile i figured out that the "NSOutlineViewSelectionDidChangeNotification" will be thrown only within the notification object. So i had to subclass my NSOutlineView to catch the notification and pass it to the object where i need it.

Your own view needs to conform to the NSOutlineViewDelegate protocol like so..
#interface MyOutlineViewController : NSView <NSOutlineViewDataSource,NSOutlineViewDelegate> {
IBOutlet NSOutlineView *myoutlineview;
}
#end
you will have this methods in your implementation
-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item;
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;
-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item;
-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
where you setup your outlineview.
When loading this view -(void)viewDidLoad gets called and your predefined nib/xib file or your manual call will set your datasource to fill it depending on your logic.
Now in your -(void)viewDidLoad your myoutlineview needs to set its own delegate with
[myoutlineview setDelegate:self];
so your own View may know where to call its notification methods triggerd from selections and so on. So you can place your notification logic inside the same View class conforming to this protocol.
-(void)outlineViewSelectionDidChange:(NSNotification *)notification {
NSLog(#"selection did change");
}

Related

why does selecting a tabbarController index programatically doesnt call delegate method

when we touch the tabbaritem of the tabbarcontroller the delegate methods are called:
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController;
but when try to do the same thing programmatically, i.e.
[self.tabbarController setSelectedIndex:selectedIndexNo];
or
[self.tabBarController setSelectedViewController:[self.tabBarController.viewControllers objectAtIndex:0]];
the delegate methods are not called. What is the reason for that?
override UITabBarController setSelectedIndex:
-(void)setSelectedIndex:(NSUInteger)selectedIndex
{
//must call super function.
[super setSelectedIndex:selectedIndex];
[self myMethod];
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
[self myMethod];
}
When you are setting them yourself via code, than you are aware that this is the time when the delegate method will be called. so whatever you wish to do you can do it at the time of setting the index programmatically. Say you want to call a method aMethod on tabbardelegate being called. you can call the method as soon as you set the index.
[self.tabbarController setSelectedIndex:selectedIndexNo];
[self aMethod];

KVO: not receiving notifications on NSTableView's -selectedRowIndexes?

I'm trying to have a custom subclass of NSTableView observe the value of its own -selectedRowIndexes property, and I'm having trouble figuring out how to receive the notifications properly. My subclass looks like this (using ARC):
#implementation MyTableView
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self addObserver:self forKeyPath:#"selectedRowIndexes" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:NULL];
}
return self;
}
- (void)dealloc {
[self removeObserver:self forKeyPath:#"selectedRowIndexes"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(#"change: %#", change);
}
#end
However, I never see -observeValueForKeyPath:... get called. Am I missing something?
I'm also open to a better solution - the reason I want to do KVO rather than relying on the delegate's -tableViewSelectionDidChange: method is that I'd like both the previous and current values for selectedRowIndexes, rather than just being able to get the current selection. If there's a way to do that without KVO on this property, I'm all ears.
If you're not seeing KVO notifications, I would open a radar at bugreport.apple.com. The reason is likely that they're not fully KVO compliant. I haven't tested, but I wouldn't be shocked.
As to how to do this without KVO, that's fairly straightforward. Use tableView:willSelectRowAtIndexPath: tableView:shouldSelectRow:. Check the current value, and the value to be added. Return YES.
I had the same problem, and I found the solution :
Bind the NSTableView view Selection Indexes to the array controller, key selectionIndexes

view-based NSTableView's views from XIB?

Is it possible to have a separate XIB file for the NSTableCellView of a view-based NSTableView? Maybe with the help of a NSViewController?
Yes, it seems to be possible.
From Apple's documentation:
In order to function, a programmatically implemented view-based table must implement the following:
...
The - (NSView *)tableView:viewForTableColumn:row: method that is defined by the NSTableViewDelegate Protocol. This method both provides the table with the view to display as the cell for the specific column and row, as well as populates that cell with the appropriate data.
This way you can have an object of the class NSView (or any subclass) and give it back, after you properly filled it with data. Where you get this object from, is not of interest. As far as I know, it would be possible to do the following, for example:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Assume you have a XIB called View.xib
[NSBundle loadNibNamed:#"View" owner:self];
// And you have an IBOutlet to your NSTableView (that's view based) called tView
[tView reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 20;
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
// Assume your class has an IBOutlet called contentOfTableView,
// your class is File's Owner of the View.xib and you connected the outlet.
return contentOfTableView;
}
Hope it works. I just threw it together having a rough idea in mind. Good luck!

How does an NSView subclass communicate with the controller?

I am brand spanking new to Cocoa programming, and am still kind of confused about how things wire together.
I need a pretty simple application that will fire off a single command (let's call it DoStuff) whenever any point on the window is clicked. After a bit of research it looks like subclassing NSView is the right way to go. My ClickerView.m file has this:
- (void)mouseDown:(NSEvent *)theEvent {
NSLog(#"mouse down");
}
And I have added the View to the Window and have it stretching across the whole thing, and is properly writing to the log every time the window is clicked.
I also have my doStuff method on my controller (this could be refactored to its own class I suppose, but for now it works):
- (IBAction)doStuff:(id)sender {
// do stuff here
}
So, how do I get mouseDown in ClickerView to be able to call DoStuff in the controller? I have a strong .NET background and with that, I'd just have a custom event in the ClickerView that the Controller would consume; I just don't know how to do that in Cocoa.
edit based on Joshua Nozzi's advice
I added an IBOutlet to my View (and changed it to subclass NSControl):
#interface ClickerView : NSControl {
IBOutlet BoothController *controller;
}
#end
I wired my controller to it by clicking and dragging from the controller item in the Outlets panel on the View to the controller. My mouseDown method now looks like:
- (void)mouseDown:(NSEvent *)theEvent {
NSLog(#"mouse down");
[controller start:self];
}
But the controller isn't instantiated, the debugger lists it as 0x0, and the message isn't sent.
You could either add it as an IBOutlet like Joshua said, or you could use the delegate pattern.
You would create a Protocol that describes your delegate's methods like
#protocol MyViewDelegate
- (void)doStuff:(NSEvent *)event;
#end
then you'd make your view controller conform to the MyViewDelegate protocol
#interface MyViewController: NSViewController <MyViewDelegate> {
// your other ivars etc would go here
}
#end
Then you need to provide the implementation of the doStuff: in the implementation of MyViewController:
- (void)doStuff:(NSEvent *)event
{
NSLog(#"Do stuff delegate was called");
}
then in your view you'd add a weak property for the delegate. The delegate should be weak, so that a retain loop doesn't form.
#interface MyView: NSView
#property (readwrite, weak) id<MyViewDelegate> delegate;
#end
and then in your view you'd have something like this
- (void)mouseDown:(NSEvent *)event
{
// Do whatever you need to do
// Check that the delegate has been set, and this it implements the doStuff: message
if (delegate && [delegate respondsToSelector:#selector(doStuff:)]) {
[delegate doStuff:event];
}
}
and finally :) whenever your view controller creates the view, you need to set the delegate
...
MyView *view = [viewController view];
[view setDelegate:viewController];
...
Now whenever your view is clicked, the delegate in your view controller should be called.
First, your view needs a reference to the controller. This can be a simple iVar set at runtime or an outlet (designated by IBOutlet) connected at design time.
Second, NSControl is a subclass of NSView, which provides the target/action mechanism machinery for free. Use that for target/action style controls. This provides a simple way of setting the reference to your controller (the target) and the method to call when fired (the action). Even if you don't use a cell, you can still use target/action easily (NSControl usually just forwards this stuff along to its instance of an NSCell subclass but doesn't have to).
you can also use a selector calling method,
define two properties in custom class:
#property id parent;
#property SEL selector;
set them in view controller:
graph.selector=#selector(onCalcRate:);
graph.parent=self;
and call as:
-(void)mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
[_parent performSelector:_selector withObject:self];
}

Which delegate method should I use to respond to clicks on an NSTextField?

I am trying to respond to a click within a textfield. When the click occurs, I am going to open a panel. My initial thought was to use a delegate method to respond to the click event - but I found that:
This method doesn't work:
(void)textDidBeginEditing:(NSNotification *)aNotification
This method does work, but only when I actually edit the text within the text field, not when I first click it. And - if I edit the text a second time, this method stops working:
(void)controlTextDidBeginEditing:(NSNotification *)aNotification
I could use as much detail as possible - or a code example, ideally. I know that an nstextfield inherits from NSControl, which has a mouseDown event. Is there a similar way to respond to the event with a textfield, also?
Since NSTextField inherits from the NSControl class, it also inherits the -(void)mouseDown:(NSEvent*) theEvent method.
I needed to have an NSTextField call a delegate function upon clicking it today, and thought this basic code might be useful. Note that NSTextField already has a delegate and that in SDK v10.6, the delegate already has a protocol associated with it. Note that if you don't care about protocols, compiler warnings, etc., you don't need the protocol and property declarations or the getter and setter.
MouseDownTextField.h:
#import <Appkit/Appkit.h>
#class MouseDownTextField;
#protocol MouseDownTextFieldDelegate <NSTextFieldDelegate>
-(void) mouseDownTextFieldClicked:(MouseDownTextField *)textField;
#end
#interface MouseDownTextField: NSTextField {
}
#property(assign) id<MouseDownTextFieldDelegate> delegate;
#end
MouseDownTextField.m:
#import "MouseDownTextField.h"
#implementation MouseDownTextField
-(void)mouseDown:(NSEvent *)event {
[self.delegate mouseDownTextFieldClicked:self];
}
-(void)setDelegate:(id<MouseDownTextFieldDelegate>)delegate {
[super setDelegate:delegate];
}
-(id)delegate {
return [super delegate];
}
AppDelegate.h:
#interface AppDelegate <MouseDownTextFieldDelegate>
...
#property IBOutlet MouseDownTextField *textField;
...
AppDelegate.m:
...
self.textField.delegate = self;
...
-(void)mouseDownTextFieldClicked:(MouseDownTextField *)textField {
NSLog(#"Clicked");
...
}
...
If you're building with 10.5 SDK, don't have the protocol inherit from NSTextFieldDelegate.

Resources