views:

2401

answers:

2

Hello. I am new to Objective C and Cocoa. I just don't get it how to message the superview of an UIView. I can't get it work. Here is what i tried so far:

In my MainView i have a method named resetDrawType:

- (void) resetDrawType {
   self.drawType = foo;
}

Also in the MainView i create a subview and add it to MainView:

mySubView *mySubView = [[mySubView alloc] initWithFrame:CGRectMake(foo, foo, foo, foo)];
[self addSubview:mySubView];
[mySubView release];

Then when the subview finished its drawing i want to send the message resetDrawType to its superview, which is the MainView.

I tried this

 [(MainView*)[self superview] resetDrawType];

and

 [(MainView*)self.superview resetDrawType];

…what didn't work. I learned about Informal Protocols so i added this code to MainView.h

 @interface NSObject ( resetters )
    - (void) resetDrawType;
 @end

But still nothing. Next i found out about this selector thing and tried this in the subview:

 if ([self.superview respondsToSelector:@selector(resetDrawType:)])
    [self.superview performSelector:@selector(resetDrawType) withObject:nil];

It also didn't work. What am I doing wrong? Thanks for your help.

+2  A: 

Well firstly in Objective-C there's no need to cast to a specific class to send it a message. E.g you can just do;

[self.superview resetDrawType];

Providing the parent view has the following function;

- (void) resetDrawType {

}

Then this should work correctly. There's no need to use informal protocols.

Also your selector testing is wrong because it does not match the definition of the "resetDrawType" function. @selector(resetDrawType:) tests for a function named "resetDrawType" that takes on parameter, which yours does not.

This is how you would test for the function above;

if ([self.superview respondsToSelector:@selector(resetDrawType)])
    [self.superview performSelector:@selector(resetDrawType)];

(Instead of performSelector you could also send the message directly).

Andrew Grant
Thanks. I tried to just send the message directly. But it doesn't get called. Does the superview have to be also the parent view?
vgbnd
Those two terms are synonymous in my mental dictionary. The superview is the view that contains your view. What are you expecting it to mean?
Chuck
+8  A: 

You shouldn't have a subviews tell its superview much of anything. You should make the superview a delegate of your view class.

// MainView
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(...) delegate:self];

Then save the delegate to an instance variable declared by id delegate. When you finish drawing:

// MyView
- (void)doDrawing {
  drawStuff();
  [delegate didFinishDrawingView:(MyView *)self]
}

Now back in your MainView, implement this method

- (void)didFinishDrawingView:(MyView *)aView;

to do what you want.

The point of all this is so that the small classes at the fringe of your app (like a small subview) shouldn't need to know how the large classes above them work. This setup allows the view to communicate up the chain, but with a message that conveys its own status instead of a message that instructs other object to do something specific. This is the way Cocoa is structured so that classes can be easily reused and their events can be repurposed however you need them to be.

Your superview should know what to do when its subview finishes. The subview should just let people know its finished.


Declare an instance variable in your subview class's header like this:

id delegate;

Write an initiailizer for your subview class that looks like this:

- (id)initWithFrame:(CGRect)frame delegate:(id)aDelegate {
  [super initWithFrame:frame];
  delegate = aDelegate;
  return self;
}

Now you have an initializer that will accept a delegate, saves that delegate, and allows you to then call methods on it.

Squeegy
Thanks! Unfortunately it still doesn't work.For this:MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(...) delegate:self];i get"warning: no '-initWithFrame:delegate:' method found"Maybe i didn't unterstand "Then save the delegate to an instance variable declared by id delegate"?
vgbnd
You have to create a new intializer method to accept a delegate (or any other variables) on init. I've updated my answer with more details about that.
Squeegy