How does an NSView subclass communicate with the controller?

时光总嘲笑我的痴心妄想 提交于 2019-12-04 09:25:32

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!