Make touch area for UIPanGestureRecognizer bigger? - ios

I add a UIPanGestureRecognizer to a custom UIView subclass, and this works as expected.
However the client now requests that the view should be easier to grab, is there a way I can make the gesture trigger from a bigger area?

Possibly the easiest way to achieve something like this is to place the view inside a container view that is the "touchable" size. It can be set to have a clear color background so it won't be visible.
Have the pan gesture added to the container and it can have whatever size you want it to have.
Another way would be to not have a pan gesture on the view but to intercept the touchesBegan on the super view and work out if the touch is inside the required "touchable" area of the view you want to pan.

Related

How to handle tap out of custom view frame

I have a custom view with custom popup on it (added as subview).
When I'm opening this popup it's frame goes out of parent view frame.
And I can't handle user interaction on the outside popup view.
How can I fix it?
I thought about this plan:
1. Add custom view on superview;
2. Add custom popup on superview (right the position where it should be on custom view)
But i think it isn't right way.
Any suggestions?
To be honest I haven't tried it yet, but you might have luck with adding a custom view on the superview and overriding pointInside:withEvent:.
I'm thinking the superview is recognizing that the tap doesn't occur inside its own frame, so it won't even check its subviews. But if you override pointInside:withEvent: you can check that the tap location is inside the subviews frame regardless of whether it's inside the superview's frame.
If that doesn't work, you might have to override hitTest:withEvent: also (or instead of). I can try to work out an example if you need more direction than that.
This Technical Q&A might help you out https://developer.apple.com/library/ios/qa/qa2013/qa1812.html
What you mention is actually the right approach: you should have a superview (it can be transparent (clear color)) that contains both the custom view and the popup inside of its frame.
On iOS, touch events are sent to a receiver at a low level in your visual hierarchy (I believe this is your Window to be specific). This receiver calls HitTest on its subviews to figure out which one to forward the event to. This happens recursively until the HitTest fails on all of a view's subviews. Then the parent of those subviews handles (or doesn't handle) the touch event.
But before the HitTest implementation even starts calling HitTest on the subviews, it calls PointInside on the superview. If PointInside returns false for the touch point, then HitTest returns null.
So overriding PointInside on the class that you call SuperView may solve the problem you're facing, but not necessarily. You may need to override PointInside on the superview of SuperView, and then on that view's superview, and so on. This is not a good solution, it is brittle and hacky. Overriding PointInside does have its uses, but this isn't one.
So try to keep all of your views within the bounds of their superview, even if you have to make a transparent superview that has no other reason to exist other than to contain other views. That's fine.

Setting the click area of a custom UIView

Is it possible to increase the clickable area of a UIView subclass?
I would rather do it without subclassing it's superview. Is that possible?
I've seen how to do it by overriding hitTest:withevent: and pointInside:withEvent: on the superview, but as I said, I would rather avoid that.
Why not create a container view that has a larger area, with a transparent background, that contains a subview that has your visible part? This is a quick and easy way to increase the tap area of a button, for instance.

Touch Event clicking UIImageView while rotating

My ImageView rotates but while it rotates it doesn't recognize touches on itself.
Do you think it's okay if I create a button via code that's over the ImageView that recognizes the touch?
When an animation is applied on any UIView or any subclass object of a UIView like UIImageView, UIButton etc then it does not detect touch events because when an animation is applied to a view, the animated property changes to its end value right away. what you are actually seeing on screen is the presentation layer of your views layer.
To answer your question, Yes, you can make a UIButton that covers up the area of the UIImageView to detect touch events on it. That sounds like the easiest to implement option in this case.
Apart from that, this link may also help you in the process. Hit testing animating layers

Propagate dragging touch to UIScrollView superview

I have been looking to all the other similar topics here, using UIGestureRecognizers, using hitTest:withEvent, pointInside:withEvent: etc. but nothing seems to be ok for what I need to achieve.
Basically I have a main view (self.view of a common UIViewController) and a small rectangular UIScrollView attached onto it at the bottom: the scrollView is filled with some UIImageViews and the user can scroll it as usual.
But the user should also be able to drag one UIImageView (or a copy of it) from the UIScrollView to the main view, and, this is what I am finding really difficult, with the SAME dragging gesture, hence I need a way to:
1) Distinguish between normal horizontal scrolling gesture, which should be handled by the UIScrollView the usual way and a dragging gesture over the image view.
2) Once identified a dragging gesture, should propagate the touch to the superview, which will host a copy of the UIImageView and WITH the SAME dragging gesture continue the dragging over the main view even out of the bounds of the UIScrollView.
Please note that I know that if the UIScrollView has userInteractionEnabled = NO the touch is propagated to the subviews, but 1) I want to propagate it to the superview not the subviews, 2) the userInteractionEnabled property apparently becomes active only once the initial gesture is terminated, while I need to use a single dragging gesture.
Thank you very much for any help.
So, so far I have ended up implementing the touchesShouldBegin:withEvent:inContentView: method of my UIScrollView subclass but with delayContentTouches set to YES (default) instead of NO as #nhahtdh was suggesting.
Strangely enough even only implementing the method was sufficient for my subviews to intercept the dragging, and still my scrollview is scrolling properly, while with delayContentTouches set to NO I was not able to scroll it as all the subviews were starting to move around.
Really the credit for this is #nhahtdh, so man, if you post an answer I will accept it, thank you very much for your help.

UIScrollview, Move down responder chain

I'll try and keep it simple.
I have a UIScrollview with around 10 images attached. I currently have it so that i can touch an image and drag it around on the scroll view.
I did this by creating the subclass UIImageview and implementing the touchesMoved etc. I can still scroll the view fine, but the problem comes when trying to drag an image too fast. It seems the program first checks if the view is being scrolled and then fires touchesMoved in the UIImage class.
Is there anyway I can switch this around so that the first check is if an image is touched, then if not pass the response onto the scrollview.
Any help would be great. Thanks.
The simplest way to do this would be use one finger to move an image, and two fingers to scroll the view.
If you're on iOS 5, this is super easy:
self.scrollView.panGestureRecognizer.minimumNumberOfTouches = 2;
If you want to support older versions of iOS, you have to do a little more work:
for (UIGestureRecognizer *gesture in self.scrollView.gestureRecognizers){
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]){
((UIPanGestureRecognizer *)gesture).minimumNumberOfTouches = 2;
}
}
If you want to use one-finger gestures for both, there are a few ways to do it. You could attach a UIPanGestureRecognizer to each image view. You might need to tell the scroll view's own UIPanGestureRecognizer to defer to the image view recognizers, using the requireGestureRecognizerToFail: message.
Another way would be to set the scroll view's UIPanGestureRecognizer's delegate to an object you create that implements the gestureRecognizer:shouldReceiveTouch: method. In that method, you can check whether the touch's view is one of your image views. If so, return NO to prevent the scroll view's pan gesture recognizer from activating.
Your question is a little confusing to me but I will assume that you have a UIScrollView with 10 UIImageViews inside it which you want to drag around.
My suggestion would be to use a gesture recognizer (UIPanGestureRecognizer) attached to every UIIImageView in order to implement the dragging behaviour. I find gesture recognizers to be a more solid approach on this kind of behavior.
If you don't know how to work with gesture recognizers, I can post a short code example to demonstrate how you can drag any type of UIView. Just ask in a comment and I will write it.

Resources