Detecting Touches outside UIView subview - ios

I'm modulating an app I'm making. I want my subview to detect touches at any location on screen either to the left or right. Now that it is a subview, I'm only able to detect touches within its bounds. I tried changing the bounds of my subview, but that causes the subview to disappear from the screen completely. Can my touch location be detected outside my subview?
UITouch *touch = [touches anyObject];
touchLocation = [touch locationInView:self.superview];
if(touchLocation.x < self.center.x) //touches occur to the left
{
right = false;
}
if(touchLocation.x > self.center.x) //touches occur to the right
{
right = true;
}

If your subview is the view which implement the code to intercept the touch, you can intercept touches just on that view.
If you want intercept touch out of the subview, you must (and is conceptually right) add the intercept code in your superview.

A gesture recognizer could work for your situation, depending on what your trying to pick up on. If your situation fits you can add one to the subviews window. You can access it via your views window property. Then inside of your gesture recognizer code you can use the "Hit testing in a view" and "Converting between view coordinates" methods of UIView to find out if the touch was in your view. Be sure to remove the gesture recognizer from the window when your object is removed from the superview or it's dealloced.

Related

Move UIView inside another UIView

How can I move an UIView A inside a square (that is another UIView B that isn't A's parent) ?
What I did: I overrode methods TouchesBegan, TouchesMoved and TouchesEnded to move A, and when A's left coincides with B's left, I avoided A to move left, but sometimes this causes A to move strangely and not smoothly (same thing for up, down and right directions), so I guess there must be a better way to do it.
If you want ViewA draggable only inside the region of a specific view. This might be easier to define if your boundaries are complicated.
- (IBAction)handleDrag:(UIButton *)sender forEvent:(UIEvent *)event
{
CGPoint point = [[[event allTouches] anyObject] locationInView:self.someView];
if ([self.viewB pointInside:point withEvent:nil])
{
sender.center = point;
//Only if the gesture center is inside the specified view will the button be moved
}
}

How to detect the touch point of iCarouselButtonDemo?

I am using iCarouselButtonDemo to create an arc button menu. I want to disable the scrolling when user touch the space other than the buttons. But now we can scroll the view by touching every point of the UIView. How can I detect the touch point of the view and disable the scrolling when user touch the outside of the 5 buttons
This is my view. This is scrolling when I touch even the bottom of the view. How can I stop it?
Thanks
In the iCarousel implementation file, you will add the following code in gestureRecognizerShouldBegin method. So it looks like this. It firstly get the touch point in the iCarousel view, and find the inner most view responding to the touch through hitTest. If the view is not a button, you stop the pan gesture.
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]])
{
CGPoint point = [gesture locationInView:self];
UIView *touchedView = [self hitTest:point withEvent:nil];
if (![touchedView isKindOfClass:[UIButton class]]) {
return NO;
}
//ignore vertical swipes

Detect touch location on UIScrollView ?

I would like to detect the (initial) touch position in my UIScrollView when the user starts dragging. I have googled this issue and many seem to struggle with this very issue. Now, while I still can't wrap my head around why Apple would not let users access touch information in a scroll view, I can't help but find a solution by myself. However all my tries failed, so I would like to ask you.
Here is what I thought would work:
I set up a UIPanGestureRecognizer like this in my UIScrollView subclass and add it to its gesture recognizers:
UIPanGestureRecognizer *tapDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(touchedAndDragged:)];
tapDrag.cancelsTouchesInView = NO;
tapDrag.delegate = self;
[self addGestureRecognizer:tapDrag];
And the corresponding method:
-(void)touchedAndDragged:(UIPanGestureRecognizer*)t{
CGPoint loc = [t locationInView:self];
//do something with location (that is exactly what I need)
//...
//Now DISABLE and forward touches to scroll view, so that it scrolls normally
t.enabled = NO;
/****
?????
*****/
}
As indicated by the comments, I would like to disable the pan gesture after I have the point and then disable the recognizer(while STILL dragging!) and "pass" the touches to my scroll view, so that the user can scroll normally. Is that feasible at all? Is there any other solution to it ?
Well UIScrollView's already have a built in pan gesture that you could tap into. Usage would be as simple as setting your class as your scroll view's delegate (to utilize scrollViewWillBeginDragging) and using UIPanGestureRecognizer's -locationInView: to determine touch location.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
CGPoint location = [scrollView.panGestureRecognizer locationInView:scrollView];
NSLog(#"%#",NSStringFromCGPoint(location));
}
Why don't you grab the start location in -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event that will be more convenient and efficient.

iOS - how to forward touches to the underlying uiview

I have 3 UIViews of the same size stacked on top of each other. The topmost is transparent and only used for detecting touches. The type of touch detected will determine which of the other two underlying views I want to receive the touch events. Once the topmost view is finished with the touch, I need to forward the touch events to the correct underlying view. How can I do that?
EDIT - I am adding my touch detection code. This is within MainViewController, whose view contains all 3 subviews.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
if (touch.view == self.touchOverlay) {
CGPoint touchLocation = [touch locationInView:touch.view];
//do a bunch of math to determine which view should get the touches.
if (viewAshouldGetTouches) //forward to viewA
if (viewBshouldGetTouches) //forward to viewB
}
}
}
Make your two subviews setUserInteractionEnabled:NO and handle all touches in the parent. Then, depending on touch type, send the correct view a programatic touch event. Then you don't need your clear view on the top. Basically you will be coordinating touch events from the bottom up instead of going top->bottom->middle.
You'll have to do this by creating a UIView subclass for your top view and overriding the following method :
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// UIView will be "transparent" for touch events if we return NO
return (point.y < MIDDLE_Y1 || point.y > MIDDLE_Y2);
}

Touch event handled by multiple views

I have a subclass of UIView on top of a UITableView. I am using the UITableView to display some data and, at the same time, I would like to overlay an animation that follows the finger (for instance, leaving a trail).
If I get it right, I need the touch events to be handled both by the UIView subclass and the UITableView. How can I do that?
Is it possible to have, ie, touchesMoved being triggered on the UIView subclass and then on UITableView?
Thank you so much for any help.
The way I have solved this problem is in a way that is not that clean, but it works. Please let me know if there's a better way to do this.
I have overridden hitTest for my custom UIView so that it directs touches to the UITableView underneath. Then in the UITableView I am handling the gestures through touchesBegan, touchesMoved, etc. There I am also calling touchesBegan on the UIView.
In this way touches are handled by two views.
The reason why I am not doing the other way around (having UIView's touchesBegan calling UITableView's touchesBegan) is that gestures recognizers on the UITableView would not work.
UIView subclass' hitTest
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// tview is the UITableView subclass instance
CGPoint tViewHit = [tView convertPoint:point fromView:self];
if ([tView pointInside:tViewHit withEvent:event]) return tView;
return [super hitTest:point withEvent:event];
}
UITableView subclass's touchesBegan
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:touch.view];
// ....
// view is the UIView's subclass instance
[view touchesBegan:touches withEvent:event];
}
No, you cann't do it implicity. Event Delivery chapter says
The window object uses hit-testing and the responder chain to find the
view to receive the touch event. In hit-testing, a window calls
hitTest:withEvent: on the top-most view of the view hierarchy; this
method proceeds by recursively calling pointInside:withEvent: on each
view in the view hierarchy that returns YES, proceeding down the
hierarchy until it finds the subview within whose bounds the touch
took place. That view becomes the hit-test view.
So, when window finds touched view it returns YES. Only one view can handle touches at the current moment.
But if you need to handle event for UITableView then handle it for UIView! You can convert touched point to required coordinates with – convertPoint, – convertRect functions, add subview to UITableView and move it depends on coordinate, and a lot of another things.
UITableView relays unhandled touch events to UIView. (Google "responder chain")
UITableView Documentation
So, you can handle your touch events in UIView only. So. In your UIView
touchesstart - do initialization stuff
touchesmove - draw tail on UIView (Use timers/delayedresponse to desable points so that it would look like a trail)
touchesend - do remaining stuff
Hope this helps.

Resources