Move UIView inside another UIView - ios

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
}
}

Related

UIButton drag finger into the bounds

I'm trying to get a particular event to work.
The user should touch one button (A), this button would pop-up some UIView, in which there is another button (B).
If the user touchUpInside button A, the popup should disappear, and the Button (B) would not be clickable.
However, if the user clicks on button (A), and then drag his finger into the button (B) then this button (B) will be selected, but only fire if the user TouchUpInside this button (B)
I have tried the most obvious TouchDragEnter and TouchDragInside, but it does not do what I expected, you have to touchDownInside first for it to work. Since the TouchDownInside event has been done on the button (A), it can't be done on the button (B)
Have I missed something, or should I just go ahead a create a subClass of my own for this particular behaviour ?
To add some difficulty, the button (A) and button (B) are not on the same UIView.
If I was to go about this, I would ignore touchUpInside etc altogether and just focus on the drag event. In the touchesMoved method, keep checking where the touches are, and if they intersect the rect of the view you are concerned about.
Something like this : (apologies for incomplete example)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self];
if ( CGRectContainsPoint(imageView.bounds, locationInView) ) {
// Point lies inside the bounds...
I have just created a library called PopMenu that implements exactly the function you mentioned. The idea is it pops out buttons when you touchdown on the menu button and keep tracking the user's finger until the user touches up on the screen. It returns the button that the user ended his touch on.

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

Detecting Touches outside UIView subview

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.

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