Thanks Andy, Jon, Kirk, Murat and everyone who responded.

All your advices were very usefull.

I decided to use the delegate pattern and everything is working perfectly now. In fact I am starting to be able to implement sevall of my planed funcionalities just by adding messages to my working delegate protocol, and my views know nothing about my document. I am tryng to make them more generic as well, so i can reuse them in other projects with similar requirements. I am also learning how to use Notifications, and will be using them soon.

Thanks again.


On Feb 18, 2011, at 9:17 PM, aglee wrote:

I would use the delegate pattern. Look at some Cocoa classes that have delegates to get the idea of the general pattern. Examples: NSTableView, NSControl, NSURLConnection. My app, AppKiDo, provides an easy way to list classes that have delegates, and to skim the delegate methods. You could also search the Xcode doc window for "delegate methods". I believe there is also documentation that specifically discusses the pattern. And Erik Buck's book "Cocoa Design Patterns" also discusses it in detail.

Here are the general steps to follow this pattern:

In MyView.h, declare a protocol something like this (typed in Mail, untested):

@protocol MyViewDelegate

- (void)myView:(MyView *)view didMouseDown:(NSEvent *)mouseDownEvent;

@end

Have MyDocument conform to the MyViewDelegate protocol:

@protocol MyViewDelegate;  // Forward declaration of the protocol.

@interface MyDocument : NSDocument <MyViewDelegate>

Add a delegate outlet to your custom view:

@interface MyView : NSView
{
//...
    id <MyViewDelegate> myViewDelegate;
//...
}

// IMPORTANT:
// Declare the delegate property as assign, NOT retain, as delegates are not retained in order to avoid retain loops.
@property (readwrite, assign) id <MyViewDelegate>myViewDelegate;

//...
@end

In IB, make the document the delegate of your custom view.

In MyView.m, send a myView:didMouseDown: message to the delegate at whatever the appropriate time is. If you're overriding mouseDown:, that's probably the right time, something like this:

- (void)mouseDown:(NSEvent *)event
{
    [myViewDelegate myView:self didMouseDown:theEvent];
}

In MyDocument.m, implement the delegate method myView:didMouseDown:.

Note that MyView doesn't have to know that its delegate is a MyDocument. It doesn't care. It just knows that the delegate is some object that wants to be notified of mouse-down events.

Also note that delegate methods should take the object doing the delegating as their first argument, and should use the name of the delegating class (in this case "MyView", hence the "myView:" at the beginning of the delegate method). You see this in all Cocoa delegate protocols. This is in case you have two MyView instances with the same delegate. The delegate can use the first argument to tell which instance it is dealing with. Passing the delegating object also allows the delegate method to get further information about it if necessary.

An advantage of the delegate pattern over notifications is that delegate methods can have return values. For example, you could have myView:didMouseDown: return a BOOL indicating whether it did anything with the event. If the superclass of MyView has a non- trivial implementation of mouseDown:, you might want to fall back on that behavior by changing the above method like this:

- (void)mouseDown:(NSEvent *)event
{
    if (![myViewDelegate myView:self didMouseDown:theEvent])
    {
        [super mouseDown:event];
    }
}

One last variation this pattern: you might want to make some delegate methods @optional.

// In MyView.h:
@protocol MyViewDelegate

@optional

- (void)myView:(MyView *)view didMouseDown:(NSEvent *)mouseDownEvent;

@end

// In MyView.m:
- (void)mouseDown:(NSEvent *)event
{
if ([myViewDelegate respondsToSelector:@selector(myView:didMouseDown:)
        && ![myViewDelegate myView:self didMouseDown:theEvent])
    {
        [super mouseDown:event];
    }
}

I hope this is not too confusing.

--Andy

On Feb 18, 2011, at 02:20 PM, Carlos Eduardo Mello <carloseme...@gmail.com > wrote:

Hi Everyone,

I am comming back to cocoa programming and to this list after a few
years without programming anything, so please forgive me in advance
for any stupid questions. I've read the docs, studied Hillegass's book
again and searched the archives, but still couldn't find a definitive
answer to my questions.

My app is document-based, started from the XCode template. I have two
custom views which are outlets of MyDocument and some extra buttons
and text fields on the document's NIB file. Drawing, actions and
events work fine for the most part. but in order to do the fun stuff
with the app, I need to acess methods in the document as a result of
mouse events in the custom views, like updating the values in the text
fields and making modifications to my data model classes (the data
engine is a collection of crossplatform C++ classes). I came across a
few suggestions in cocoa-dev's archives from a few years ago, but they
didn't seem quite it.

1. Create outlets in the view's subclasses and point them to
MyDocument using IB;
2. Create outlets as in 1., but adding acessor methods to initialize
them;
3. accessing MyDocument with messages to the class hierarchy , as in
[[[self window] windowController] document];

I tried to use a mix of these approaches, or in other words: query the window controller for the document and then store its reference inside
the views forq quicker access. Here's where I get stuck:

In order to use 1. or 2., I need to declare an instance of MyDocument
inside my view's classes. The compiler won't let me do this unless I
import MyDocument to the class definitions. The problem is that the
classes are already included in MyDocument and the "chicken/egg" thing
makes XCode spit a zillion compile errors (naturally). I tried
declaring the variables inside the custom views as type 'id' and
tested the approach calling a MyDocument method which shows some data
in various textfields. The code compiled and ran, but MyDocument never
got the messages.

// inside custom view's class definition
id document;

// inside awakeFromNib
document = [ [ [ self window ] windowController ] document ];

// inside mouseUp
[document refreshParameters];

If I just query the window controler for the document on demand like
this:

[ [ [ [ self window ] windowController ] document refreshParameters ];

the code works, but I still can't get rid of those "message-not- found"
warnings:

"warning: no '-refreshParamters' method found
(Messages without a matching method signature will be assumed to
return 'id' and accept '...' as arguments.)"

So my questions are:

1) Is there a better way to approach this?
2) Is it OK to just call the document like this and ignore the
compiler warning?

I'd really appreciate if someone could comment on this - i'd hate to
find out later that i'd been building on a bad design...
Thanks already for a y help.

Carlos.


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/aglee%40mac.com

This email sent to ag...@mac.com

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to