问题
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.
回答1:
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.
回答2:
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).
回答3:
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];
}
来源:https://stackoverflow.com/questions/6891030/how-does-an-nsview-subclass-communicate-with-the-controller