Sage Washabaugh
Sage Washabaugh

Reputation: 297

Call an IBAction method in two classes

Im trying to call an IBAction Method in two classes that is called whenever a button that I created in interface builder is clicked. What I really want to happen is for a NSRect to appear whenever the button is clicked but the button and the place where I want the NSRect to appear are in separate views, so the button is in View A and the destination for the rect is in View B. I have tried doing this with NSNotificationCenter but it did not work.

Upvotes: 0

Views: 1013

Answers (3)

Rob Keniger
Rob Keniger

Reputation: 46028

You are missing the C in MVC. Cocoa uses the Model-View-Controller design pattern and you seem to be missing a controller.

You should create a controller class (possibly a subclass of NSWindowController so that it is responsible for the window) that implements an action method such as -buttonPressed: which is connected to your button. The controller should manage the model (which in this case is whatever the rectangles represent) such that when you press the button, the model is updated. The controller should then make your rectangle view redraw itself.

Your rectangle view should be set up so that it implements a datasource pattern (see the NSTableView datasource implementation for a good example) so that it knows how many and where to draw the rectangles. If you set your controller as the view's datasource, your view doesn't need to know anything about the model.

Your rectangle view should be set up something like this:

RectangleView.h: @protocol RectangleViewDataSource;

@interface RectangleView : NSView
@property (weak) id <RectangleViewDataSource> dataSource;
@end

//this is the data source protocol that feeds the view
@protocol RectangleViewDataSource <NSObject>
- (NSUInteger)numberOfRectsInView:(RectangleView*)view;
- (NSRect)rectangleView:(RectangleView*)view rectAtIndex:(NSUInteger)anIndex;
@end

RectangleView.m:

@implementation RectangleView
@synthesize dataSource;

- (void)drawRect:(NSRect)rect
{
    //only draw if we have a data source
    if([dataSource conformsToProtocol:@protocol(RectangleViewDataSource)])
    {
        //get the number of rects from the controller
        NSUInteger numRects = [dataSource numberOfRectsInView:self];
        for(NSUInteger i = 0; i < numRects; i++)
        {
            NSRect currentRect = [dataSource rectangleView:self rectAtIndex:i];
            //draw the rect
            NSFrameRect(currentRect);
        }
    }
}
@end

You would assign your controller as the datasource of the view and make it implement the RectangleViewDataSource protocol methods.

The controller would look something like this:

YourController.h:

#import "RectangleView.h"

@interface YourController : NSWindowController <RectangleViewDataSource>
@property (strong) NSMutableArray* rects;
@property (strong) IBOutlet RectangleView *rectView;

- (IBAction)buttonPressed:(id)sender;

@end

YourController.m:

@implementation YourController
@synthesize rects;
@synthesize rectView;

- (id)init 
{
    self = [super init];
    if (self) 
    {
        rects = [[NSMutableArray alloc] init];
    }
    return self;
}


- (void)awakeFromNib
{
    //assign this controller as the view's data source
    self.rectView.dataSource = self;
}

- (IBAction)buttonPressed:(id)sender
{
    NSRect rect = NSMakeRect(0,0,100,100); //create rect here
    [rects addObject:[NSValue valueWithRect:rect]];
    [self.rectView setNeedsDisplay:YES];
}

//RectangleViewDataSource methods
- (NSUInteger)numberOfRectsInView:(RectangleView*)view
{
    return [rects count];
}

- (NSRect)rectangleView:(RectangleView*)view rectAtIndex:(NSUInteger)anIndex
{
    return [[rects objectAtIndex:anIndex] rectValue];
}


@end

Upvotes: 2

Chuck
Chuck

Reputation: 237110

You want to have a controller that knows about the view with the rects. Hook the button up to the controller. When the button is pressed, add a rect.

Upvotes: 1

TheAmateurProgrammer
TheAmateurProgrammer

Reputation: 9392

Are they in separate windows? If they aren't, just declare an IBAction in your ViewB and connect it to the button in ViewA. If they are in different windows, the NSNotificationCenter way should work. Did you use postNotificationName: and addObserver:selector:name:object with the same notification name?

Upvotes: 1

Related Questions