Pass touch from UIScrollView to superview (UIButton) - iphone

Possibly a duplicate, but I couldn't find an exact answer to my problem searching SO tonight...
I have a UIButton which contains a UIScrollView, so the button is the superview.
All I want to do is pass a single-tap event from the UIScrollView subview to the UIButton superview, and have the UIButton handle it as it would if the user tapped the button directly.
Setting the scroll view's userInteractionEnabled property to NO won't work for me because there is content the user can scroll. Doing so would defeat the purpose of the scroll view.
Is this possible? Thanks!

Like huoxinbird said, it's definitely not common to lay your views like that.
In the case where you are solidly adamant about this, you could use UITapGestureRecognizer and have it call the button's target and selector.
If you wanted to include the button highlight and such, then you may have to subclass UITapGestureRecognizer and forward the touchesEnded to the button.

Create a subclass of scrollview and override
-(BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
{
return YES;
}
Also subclass the uibutton(Your superview) and handle touches in touches began.

Usually you don't put scrollview within a button. Instead consider using a UIView as the superview of scrollview. Or tell us more about your use case, so I can understand why you have to design in this way.

Related

Gesture is not working when used on transparent UIView when subclassed to detect objects behind it

Hi all,
I am having some issue with detecting touches. Please refer image where yellow and brown are my UIView which is subclassed to detect the transparent touches. I have added three gesture recognizers pan, tap and rotation on that UIViews, but when I subclass UIView and override the - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event method to detect all the Imageviews added, gesture recognizers stop working outside the ImageView. And if I am not using subclass then it's unable to detect touches for ImageView on the yellow UIView. I tried the solution from this link
Forwarding UIGesture to views behind
though it is not the same as my requirement, but it didn't help! Any help/hint is appreciated! Thanks in advance.
I have resolved the issue myself. I achieved the my target by applying gesture recognizers on superview not separate for each uiimageview or the colored uiviews. I have added uiviews on superview and now gesture is getting applied to imageview also. And also changed the code in gesture actions so that gesture will get applied to touched imageview and not on superview.
Try implementing gestureRecognizerShould begin in your subclass, test to see if the class of the recognizer is those classes [gestureRecognizer class] == [UITapGestureRecognizer class] for example, and return yes if it is.
http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/gestureRecognizerShouldBegin:
The only way I've found to fix this is to add your stack of views as children to a UIView (A). It should be the same size as your transparent view/ all of the child views on your stack. You should implement the gesture recogniser in the view controller if and connect (A) as the iboutlet (just drag and drop a gesture recogniser onto A in interface builder for example).
(View Controller)
|->(A)
|->(1) Need gesture
|->(2) Need another gesture
|->(3) Transparent (messing everything up)
Connect the gesture handlers to the appropriate children of A who need to handle them.
For View Controller
#interface AUIViewController : UIViewController <bla, bla, UIGestureRecognizerDelegate >
For (1)
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer{/*yada yada*/}
In my opinion, this is an apple bug.
My answer is pretty much this but it is a work around for what is in my opinion, a bug.
P.S, sorry I didn't fix this without using interface builder. Hope its still helpful

Zoomable UIScrollView with UIButtons capturing touches

I have a UIScrollView and I'm implementing the viewForZoomingInScrollView: delegate method which returns a UIImage which the user can zoom and pan. I've also got some UIButtons as sub views of the UIScrollView which I'm using as annotations, like Google Maps.
The problem I'm having is that a lot of the UIImage can be obscured by the UIButtons when zoomed right out. When trying to pinch to zoom the UIButtons are receiving the touch event instead and the zoom is not happening. You end up having to carefully place your fingers in clear space to zoom.
I note the Google Maps app seems to work ok when there are lots of annotation views, you can still pinch.
I guess I need to priorities the touches, the UIScrollView needs to respond to pinches and pans, while the buttons just taps.
Anyone have experience of this?
I had this exact issue, and it was kind of weird. It wasn't due to any gesture recognizer on the button, it was the UIScrollView's pinch gesture recognizer that was being forwarded to the UIButton for some reason. Also, the UIButton was responding to certain UIGestureRecognizerDelegate calls (like -gestureRecognizerShouldBegin:) but not others (like -gestureRecognizer:shouldReceiveTouch:). It's like the UIScrollView was selectively forwarding some delegate calls to the UIButton and some not.
I finally found a very easy way to solve this issue:
yourScrollView.pinchGestureRecognizer.delaysTouchesBegan = YES;
yourScrollView.pinchGestureRecognizer.delaysTouchesEnded = YES;
Luckily, the default pinch gesture recognizer on UIScrollView's are publicly accessible, and the two delaysTouches property are NO by default. When you set them to YES, if the gesture recognizer could possibly or does recognize a pinch gesture even when it starts on top of a UIButton, it won't forward those touches to the UIButton, and the UIButtons will no longer interfere with the UIScrollView's zooming.
try this
UIGestureRecognizer* tapRecognizer = nil;
for (UIGestureRecognizer* recognizer in yourButton) {
if ( ![recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
[yourButton removeGestureRecognizer:recognizer];
break;
}
}
so you remove all the gesture recognizers from the button different from UITapGestureRecognizer
take a look at UIView's hitTest:withEvent: method. Inside that method youll need to check for which view you want to return. The view you return will be the one recieving the touches. for example, you can subclass the button and override that method to return the ImageView for your particular scenario.
I fixed this problem in a different way, but this might not be your case.
My buttons were added on an image view, the image view being added by itself to another container view. In the:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
... method, I returned the image view - bad. Changing to return my very first container view in hierarchy fixed the touches.

Touch events passing to superview through transparent sections

I have a simple app which adds a subview over the main view when the user clicks on a UIButton in the main view. This subview is of size 480x320 (I'm in landscape mode), but there is a boarder around the centre image in this subview which is transparent.
This is where my problem lies. I would like only the subview to process touches until it is removed from the superview, but if there is a touch event on the transparent boarder, the event gets passed to the superview, and ignores the subview, even though the subview is the full size of the window.
Doing some research into this, it seems as though this is what apple intended to happen, as touches will only get passed to opaque sections, even if the subview is the full size of the window. It is explained in the reference:
http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MultitouchEvents/MultitouchEvents.html
I would like to be able to set it such that the touches will stay with the subview, regardless of any transparency issues. The hitTest:withEvent: method seems to do something like this, but more for passing touches to different subviews then the one that was touched.
Is there anyone who has a fix/work-around that can achieve this?
Simple solution is to subclass your subview and add the following empty method...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
}
This will prevent the touch from getting passed to the superview, even if the user were to touch a transparent part of the view.
You should just put a clear button in the back of the xib. That will prevent touch events from going off.
If you make the backgroundColor of your subview hidden, you will obtain the same effect (a transparent border around the UIButton), and your view will receive touches:
You can execute this code just before adding the subview:
subview.backgroundColor = [UIColor clearColor];
subview.hidden = NO;
[parentView addSubview:subview];

How to get a UIView under a UIScrollView to detect touches?

I have a UIScrollView ontop of my UIViewController recreating an effect like in the Gowalla iPhone app when you're on a spot's page. Under my scroll view I have a button that I want to be able to perform it's action even when the scroll view's frame covers it up (where it's ontop of the button, the scroll view's clear). How would I do something like this? Is it possible? (it has to be, Gowalla [somehow] did it)
As for me, I will transfer touch event to another view by override following methods.
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
Like this way,
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// pass touch event by default implement.
[super touchesBegan:touches withEvent:event];
// redirect the touch events.
[anotherView touchesBegain:touches withEvent:event];
}
hitTest:withEvent: is used to decide which view should response touch event on the view hierarchy tree. If the view doesn't want to response touch event, it will return nil in hitTest. As result the above touch event methods won't be called.
I have no idea what this "Gowalla" app might do; hopefully, your description of "a button behind the scroll view, that you can touch 'through' the scroll view" is accurate.
If your button behind the scroll view can be sized to fill the entire contentSize area of the scroll view without screwing up your interface, the easiest solution would be to just put it inside the scroll view (under all the other views) and do just that.
Otherwise, your best bet is probably to create a custom view with a clear background to be placed in the scroll view as above. The easy solution is to have the custom view (probably a UIControl) just do whatever touching the button does. If that's not possible for some reason, your best option would be to override hitTest:withEvent: on the custom view to return the underlying button. I'd be wary of overriding hitTest:withEvent: on the scroll view itself, as that might interfere with scrolling.
Try adding the button on top of the scroll view. The only problem with that is if you hit the button, you will not be able to interact with the scrollView, but it will still be visible.

How to detect tap on uiview with lots of controls?

My problem is about tap detection.
I have a uiviewcontroller and there are some controls on uiview (labels, buttons, tableview, imageview, etc..)
When I tap the uibutton I display a small uiview (200x150), if the user taps the uibuttons in smallview I hide the smallview.
But I can't hide the uiview if the user taps the background.
I tried this code..
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//NSLog(#"Touches began.");
[self hideShareView];
}
It doesn't work if I tap the another button in the uiviewcontrols view.
I just want my uiviewcontrol's uiview to react first.
I think its about firstResponder but I dont know how to set it first.
edit: i want it to work like a uiPopover in ipad.
I believe that the correct approach is to add a new transparent UIView when you display your "smallview". You should add a UITapGestureRecognizer to that UIView, in order to trigger the desired selector when the tap is detected. Also, you must ensure that the views are arranged properly, with your smallview being the one at the top, the transparent UIView being immediately below and the rest of the view hierarchy below the transparent UIView.
Finally, you should ensure to remove the transparent UIView from your view hierarchy at the same time that you remove your smallview.
Does that make sense?
Please have a look at this question (how-to-make-a-superview-intercept-button-touch-events), it does seem highly related.
try your hands with bringing your small view (i.e. shareview) to front or sent your main view behind your small view.
If it still doesn't work & you don't want your main view to perform any action when smallview is opened then try
[<YOUR_MAIN_VIEW> setUserInteractionEnabled:NO];
, but MAKE SURE you can do this only when you don't want your main view to perform any action when smallview is opened