UIResponder chain question - objective-c

Im not too informed on the nitty gritty of modifying the responder chain, so if this is stupid pls dont bash me ;)
basically i have 2 view stacks(blue) as a subview of a parent view(red)
they are both occupying the full frame of parent at one point in time, so obviously only the one on top is getting touch events, which travel upstream to the parent view(red) and on to window.
During some cases i want the touch input to be picked up by the eligible child view of 'other' view stack. That is, the view that woud be receiving these inputs if the current topmost view had userinteractionenabled set to no.
Setting userinteractionenabled works but i feel like its a dirty hack. The gist is this topmot view is mostly transparent and i want the events when touched in the specified region to end up on the other stack.
here is a picture to help visually explain, and keep in mind both blue views are 100% of parent.
http://c.crap.ps/35a5

You can override hitTest:withEvent: in each of the views to control who gets to "consume" the touch.
In your example I am assuming that the greenish areas are subviews that you want to consume the touch events. If so, then try this for the hitTest method:
-(UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
return (hitView == self) ? nil : hitView;
}
This method checks if the touch hits any of the subviews. If it does then it lets that subview consume the touch, otherwise it lets the touch continue through the hierarchy.

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.

Cocoa NSView: overlapping view's doesn't block controls underneath

I have 2 views 1 view has NSTextFields (viewA) the other one (viewB) just overlaps viewA
but i can still on the textfields even though viewB completly overlaps the textfields.
is there an easy way to block this ?
Mouse events go to whoever will accept them, so put
- (void)mouseDown:(NSEvent *)theEvent { }
in View B.

Detecting all touches in an app

In an iPad app, wherever a user is touching the screen I want to display an image, highlighting the points they are touching. The app contains a number of nested views, all of which should receive touches and behave normally.
Seems simple, but I've failed to find a good way to do it. Using the touches began: with event and related functions on the root view controller does not work because if a subview is touched the events are not fired. I've also created a 'dummy' gesture recognizer which just passes touch events to another class which draws images. That works great-ish and buttons work, but breaks UIScrollViews and I'm guessing other subviews with gesture reconizers.
Is there nowhere you can just access all touch events without affecting where those touches are headed?
thanks.
Your dummy gesture recognizer should be ok. Just watch out for setting states. possible -> began -> ...
Basically your gesture recognizer is forwarding all touches so it can be in began or possible state all the time while any touch exists.
To get rid of problems with other gesture recognizers return YES in this delegate method.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Other option is to subclass main UIWindow in your app and override this method
- (void)sendEvent:(UIEvent *)event
Here you should have access to all events. It's quite easy to filter them.
You can apply a UITapGestureRecognizer to the entire view and set the cancelsTouchesInView property to NO. This will allow you to be notified of all taps on the view and its subviews without intercepting all of the touch events.
Additionally, you can implement the -gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: delegate method to keep this gesture recognizer from stomping on the ones used by views like UIScrollView.
you can try overriding hitTest:withEvent:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
This maybe what you are looking for.

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.

Objective C: Can I set a subview to be firstResponder?

I have a situation whereby I am adding a view from another viewcontroller to an existing viewcontroller. For example:
//set up loading page
self.myLoadingPage = [[LoadingPageViewController alloc]init ];
self.myLoadingPage.view.frame = self.view.bounds;
self.myLoadingPage.view.hidden = YES;
[self.view addSubview:self.myLoadingPage.view];
Is it possible to set 'self.myLoadingPage' to be the first responder? This is the case whereby the loadingpage view size does not cover the entire size of the existing view and users can still interact with the superview (which is not the desired behaviour). I want to just enable the subview in this case.
When I had a similar problem, I made an invisible UIView that covered the entire screen, I added the large invisible UIView on top of the main view and made the loading view a subview of the invisible UIView.
The simplest solution is to override hitTest method in your loading view to return TRUE. This top view is first in the responder chain, the hitTest method gets called which NORMALLY returns TRUE if the point is within the view and will therefore be handled, returning TRUE regardless means you get the touch event and effectively block the message being resent to the next responder.
Interesting question. I found a similar post with a quote from the Apple Developer Forums on this issue:
To truly make this view the only thing
on screen that can receive touches
you'd need to either add another view
over top of everything else to catch
the rest of the touches, or subclass a
view somewhere in your hierarchy (or
your UIWindow itself) and override
hitTest:withEvent: to always return
your text view when it's visible, or
to return nil for touches not in your
text view.
This would seem to indicate there isn't a terribly straightforward solution (unless there was an API change regarding this made after October, 2010.)
Alternatively, I suppose you could go through all the other subviews in your superview and individually set their userInteractionEnabled properties to NO (but that would probably prove more cumbersome than the quoted solutions).
I would love to see other ways to allow this.