Sending messages from subview to superview in Cocoa, UIResponder? - objective-c

I have all these subview which are touch sensitive, I want to send a message from the subview to the superview, to say that a user selected it, so the superview can communicate with the rest of the controller.
I can not communicate between the subviews and the controllers,
subviews >> superview >> controller
Perhaps use, UIResponder to achieve this?

Depending on the subviews, the target/action mechanism can be good for this. If you can derive your subviews from UIControl, then it's particularly easy to have your subviews send their action message to their target, which is usually your view controller. You'll be able to lay out your views in Interface Builder and specify their target and action by connecting them to the view controller. If you can't derive them from UIControl for some reason, then you'd have to implement the equivalent of target/action and you won't have the same support in IB, but it's still pretty straightforward.
Another possibility is to let the view controller do the touch handling for all the subviews. This is basically taking advantage of the responder chain as you suggested, but at the touch-handling level. It may not be ideal if there are a lot of subviews to keep track of, but it's workable.
A third way to do it is to have your subviews post a notification when they're selected.
As is, UIResponder doesn't provide a mechanism for passing arbitrary messages along the responder chain. I'm not sure that adding that capability is the most elegant way to send a message specifically from a subview to the view controller. There are potentially many intermediate objects between the view controller and the subviews, and involving the entire chain when you already know where you want the message to go seems wrong. However, it's interesting to think about extending UIResponder to make the responder chain a conduit for more than just events. You could add a category to UIResponder:
#interface UIResponder (Messages)
- (void)sendMessage:(SEL)message withObject:(id)object;
#end;
#implementation UIResponder (Messages)
- (void)sendMessage:(SEL)message withObject:(id)object
{
if ([self respondsToSelector:message]) {
[self performSelector:message withObject:object];
}
else {
[[self nextResponder] sendMessage:message withObject:object];
}
}
#end
WARNING The code above is completely untested, and may be a lousy idea for reasons I haven't yet thought of. Proceed with caution. Expect compilation errors. Cross your fingers. Please let me know if it works out well, and leave me alone if it doesnt.

Why can't you use [self.superview sendMessage]?

Related

IOS Accessing one view from another view

Basically I have a view controller having two subviews. I want these views to be connected. A touch event should trigger an event from another view and vice versa. I have thought about two solutions.
1-) Accessing views through their view controllers
2-) Each view has a pointer to another view
I am a newbie on IOS and as far I read from other problems it is mentioned that accessing view controller from a view is not suggested. So, what do you guys suggest me to do?
Edit:
I didn't make much progress on coding but my first view is:
#interface PaintView : UIView
-(id)initWithFrame:(CGRect)frame andController:(ViewController*)ctrl;
and i will control the touch event and access my viewcontroller:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//[self.controller somethingThatAccessToOtherView]
}
and second view will be very similar to that one.
Why dont you use the delegate pattern!
For the two views communicate with each other ..
Its essentially similar to " Each view has a pointer to another view" but in a much more flexible manner
here is a so thread on it and
If you want the simplest, dirtiest, and non recommended way of proceeding. Tag your two views with some sort of unique tag (use say, -20 and -21, or something. So in the view controller where you create the views you do the following.
[v1 setTag:-20];
[v2 setTag:-21];
Then you can do from in say, v2, the following.
[self.superview viewWithTag:-20];
to get a reference to v1. However, this assumes the same superview, and is not a nice way of doing things unless you are sure that the view heirachy will not change (I'm talking, it's a widget you made that no one else is going to touch and you've documented it well anyway).
A better way would be to use a delegate pattern. The ViewController is the delegate of each of the subviews. On touch, the subviews call some method like
[delegate iwastouched:self];
and the delegate has a method like
-(void) iwastouched:(UIView *) someview {
if(someview == v1){
//view 1 was touched, do something to view two
}
if(someview == v2){
//view 2 was touched, do something to view one
}
}
Another bad way of doing it would be to use notifications. Hell, there are about as many ways to do this as you could like. Some are just not as nice.
Instead of doing this, in the subclass of UIViewController, you should have two members. These member will represent each of the subview.
Now, inside the subclass of UIViewController, add the methods of touch which are of your interest. Inside this methods, identify the view on which any touch event is generated.
Depending on it, pass the event to other view.
You should not have UIViewController as a iVar of your UIView subclass.

Set a subview as the responder to calls to a parent UIView subclass

I've got a UIView that I've subclassed to be the main view used throughout my app. In it, I have two subviews: banner and container. Banner is basically a place to put an ad or a disclaimer or whatever. Container is meant to act as the primary view, to which you can add, remove and whatever as if it were the only view.
Right now, I'm just overriding the methods of the parent view and sending the calls to the container view. I'm wondering if there is an easier way to do this, without having to write out stuff like this for every method:
- (void)addSubview:(UIView*)view {
[container addSubview:view];
}
Maybe something that lets you delegate all method calls to the view to a specific subview, rather than responding to the method calls itself.
Anyone know if this is possible?
I'm a little confused by the question.
The responder chain is present and passes ui events up through all visible views on screen, by hierarchy. It may be useful to read a little about the responder chain, because by design it passes events from the deepest view to the highest (root) in that order, which is the opposite of the direction you're seeking (if I'm reading this right).
If you need to forward events from a superview to a subview, to respect principles of encapsulation, you should define appropriate actions in your subview's subclass interface, and then then configure your superview to target the actions in that subview/class.

Listening for removal of view from superview in NSViewController

I have an NSTextField in an NSView that is controlled by an NSViewController. At some point, I remove this view. When the view is removed, if the text field has focus, it will dispatch an action. Because I'm in the middle of deconstructing the data and the view, it causes a fatal exception.
I think the answer to this would be to listen for when the view is being removed from the superview in the NSViewController and then remove the view controller as the target for the action. Is there some easy way to do this that I'm missing?
I think the easiest way is to override the willRemoveSubview:(UIView *)subview method of your NSView. You should be able to do whatever you want in your implementation to prepare yourself for the removal that is about to happen.

Get UINavController from subview of UIView?

I added a custom subview to a UIViewController's content view. From that subview, how can I get a reference to the superview's controller?
Thanks
The correct answer is "You're Doing It Wrong™" ;-)
You shouldn't need to reference to a view controller from a view, and you certainly should never retain a view controller in one of your views -- or you'll end up with a retain loop and leak memory.
Cocoa provides multiple patterns to solve this problem:
Use a delegate: Define a protocol called DemoViewDelegate and add a delegate property to DemoView. Then have your view controller implement this protocol. Important: delegates should never be retained! Any delegate property you create should be set to assign. See Apple's Delegation docs or just google "delegation pattern".
Use the responder chain: Call UIApplication's sendAction:to:from:forEvent: and leave to: set to nil to have your action message automatically routed up the responder chain to your view controller. See Apple's Responder docs and the more detailed Action Messages docs.
Use a notification: Less common in this particular scenario, but you could also have your view controller listen for a notification, which the view sends.

Why does an NSWindow or NSView instance handle its own key events, and not its delegate?

As a newcomer to Cocoa, I am struggling to understand why the generic NSResponder subclasses implement key events the way they seem to do.
In my program, I have an NSWindow subclass which takes up the whole screen, and must necessarily handle key events. There are several major commands which can change the whole state of the program (e.g. pause a timer when the user hits the spacebar) which it does not make sense to have subviews like an NSTextField handle.
It seems to me that the delegate (controller) should get these events. Instead, I find I have to either write a bunch of messy glue code to have the window (via its keyDown: and interpretKeyEvents: selectors) notify the controller, or I have to just move a bunch of controller code to the NSWindow subclass itself.
This is messy and my gut tells me I'm missing something. Is there a cleaner solution?
If you've set it up correctly, the NSWindow's delegate will receive the messages. Cocoa uses the responder chain to forward messages from the first responder -- the key view for key messages, and the view that was clicked/hovered/etc. for mouse messages -- back through the superviews, up through the window, and eventually to the window's delegate. There's a pretty good diagram of the typical responder chain on Apple's site.
You really should never have to subclass NSWindow unless you're implementing some fancy window drawing or something else along those lines. Cocoa provides the NSWindowController class to behave as a controller for a window and its contents.
The usual pattern is to subclass NSWindowController and add your IBOutlets to it, and then use a NIB to lay out your window contents. You make your NSWindowController subclass the class of the File's Owner proxy in Interface Builder. And you also assign the window's delegate to the window controller so that it can become part of the responder chain. Finally, to create windows, you use NSWindowController's initWithWindowNibName: method, which automates loading the NIB with a new window controller as the file's owner.
I'd recommend reading up on window controllers in the Cocoa documentation, because they provide the missing link you're looking for.