Detecting which uiscrollview user touches - ios

I have an iPad application in which two scroll views are next to each other. I have UiImages inside of both so i need to know where the user touches whether it's the first scroll view or the second one. Is there any way i can track user's touch position whether he hits the scrollview A or B ?

You can add UITapGestureRecognizer to your scrollviews, like this:
UITapGestureRecognizer *tapGuestureForA = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureCapturedForA:)];
[scrollViewA addGestureRecognizer:tapGuestureForA];
same thing can be done for scrollView B, and when tapGestureCapturedForA is called, it indicates user touched scrollView A.

Related

How to synchronise UIView moving with swipe gesture of UIPageViewController

Ok, here is the thing:
I need to have few animations happening on every screen change in my PageViewController.
So, when a user swipes, an image should fly in from the top left corner, from example.
I can make that animation to happen over time when user swipes and changes the page, but what I need is to that animation to be synchronised with the swipe movement itself.
So if a user presses the screen and starts swiping, the animation should follow user's finger and animate it's translation with the finger movement.
How can I achieve that?
I guess I need some sort of swipe gesture listener, but I failed to find any solution online. I guess I'm not using the right keywords.
You can use a UIPanGestureRecognizer to get the number of pixels swiped:
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panRecognizer_Panned:)];
[self.myViewToSwipe addGestureRecognizer:panRecognizer];
...
- (void) panRecognizer_Panned:(UIPanGestureRecognizer *)recognizer {
CGFloat pixelsMovedHoriz = [recognizer translationInView:self.vwRelativeTo].x;
}

Set exclusive touch on multiple UIViews of the same class

I am creating a random number of custom UIViews of the same class, and I'm adding them in the UIViewController's view. I'm assigning them a UITapGestureRecognizer, but I can't seem to make the exclusive touch work:
for (int i = 0; i <= n; i++) {
ICCatalogProductView *catalogProductView;
catalogProductView = [[ICCatalogProductView alloc] init];
[self.view addSubview:catalogProductView]
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(testTouch)];
[catalogProductView addGestureRecognizer:tapGesture];
[catalogProductView setExclusiveTouch:YES];
}
If i tap the UIViews simultanously, the method is called twice (not the behaviour I want). Is there any elegant method of solving this, or any method at all?
From the Apple Documentation:
exclusiveTouch only prevents touches in other views during the time in
which there's an active touch in the exclusive touch view. That is, if
you put a finger down in an exclusive touch view touches won't start
in other views until you lift the first finger. It does not prevent
touches from starting in other views if there are currently no touches
in the exclusiveTouch view.
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.
means its only set exclusive in your one view, not if you are touching something outside your view.

Disabling UIGestureRecognizers in underlying ViewController

In a View Controller I'm building a grid of icons.
Each icon opens the same pop-up view , but filled in with different information.
I'm creating the grid in this way:
for (int i=0; i<NUM_BADGES; i++) {
BadgeThumbView *thumb = [[BadgeThumbView alloc] initWithFrame:CGRectMake(posX, posY, 70, 100)
andWithLabel:[NSString stringWithFormat:#"BADGE NAME N. %d", i]];
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onBadgeTapped:)];
[thumb addGestureRecognizer:gestureRecognizer];
[thumb setTag:i];
[more code here....]
}
And in onBadgeTapped method I'm creating the pop up.
Now my problem is that everything works fine, but I just realized that when the pop up is opened, while interacting with its buttons I'm still triggering the gesture recognizer in the underlying view controller.
Is there a way to disable all GestureRecognizers in underlying view? Is my strategy wrong? And: is there a way to use a single UIGestureRecognizer for all my icons, in order to disable/enable in a easier way?
Thanks a lot
You can remove the recognizer from view or set userInteractionEnabled to temporarily disable it. Depending on how your popup is implemented, you may be able to disable them all at once.
One solution would to be adding thumbs as a subview of a container UIView, and adding that container to your parent view. You could then enable/disable all by setting userInteractionEnabled on the container view.
I think you should do some thing like disabling the userInteraction for all thumb views when the popup is appearing and re-enabling when disappearing like this
[[yourSuperView subviews]makeObjectsPerformSelector:#selector(setUserInteractionEnabled:) withObject:[NSNumber numberWithBool:FALSE]];
Else add all thumbViews to one subview( say 'b') then add view 'b' to superview(say 'a') as subView and turn off the user interaction to view b when popup appeared and turn on when popup disappears

Detect touch event on UIScrollView AND on UIView's components [which is placed inside UIScrollView]

I have UIViewController on my simple storyboard IPad project which contains UIScrollView which is placed over entire surface (1024 x 768). I have created 3 XIB files which are UIViews which my application loads on start in viewDidLoad and add them into UIScrollView. Each of these 3 XIB files contains only one UIButton.
This is hierarchy:
~ UIViewController (UIViewControllerClass is class for this
UIViewController)
~~ UIScrollView (contains 3 identical UIViews)
~~~ UIView (UIViewClass is File's Owner for this XIB file)
~~~~ UIButton
I would like that my UIViewControllerClass becomes aware of both: touch anywhere on UIScrollView component AND if UIScrollView is touched, if UIButton inside of UIView in UIScrollView is touched, to have information that exactly that button is touched.
I made IBAction inside UIViewClass for touch on UIButton inside UIView in UIScrollView and when I set User Interaction Enabled = YES on all elements (UIViewController, UIView and UIScrollView) this method is called as it should.
But at this point my UIViewControllerClass isn't aware that touch occurred inside UIScrollView on that UIButton. I made touch recognizer like this:
UITapGestureRecognizer *touch = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTouch)];
touch.numberOfTouchesRequired = 1;
and added it to UIScrollView component. In this way I am able to detect touch event on UIScrollView component in UIViewControllerClass, but touch event handler for UIButton in UIView which is inside UIScrollView isn't called anymore.
So, I need to have these two informations in UIViewControllerClass:
Touch on UIScrollView component was made
Touch on UIButton in UIView which is inside UIScrollView (if this button was touched) was made
I suppose that attaching touch event recognizer to entire UIScrollView component isn't solution, since it disables all touch event handlers I wrote inside UIViewClass.
I think solution is that somehow touches which are made on components in UIView inside UIScrollView should be sent up to UIViewControllerClass, but I didn't found a way to do this.
If anyone can help me, I'd be very grateful. Thanks in advance.
[edit #1: ANSWER by Zheng]
Tap gesture must have cancelsTouchesInView option set to NO!
For my case above, this line solves everything:
touch.cancelsTouchesInView = NO;
Many thanks to Zheng.
I don't know if this works for you or not, but I've given an answer about touch events for views inside scrollview here:
Dismissing the keyboard in a UIScrollView
The idea is to tell the scrollView not to swallow up all tap gestures within the scroll view area.
I'll paste the code here anyways, hopefully it fixes your problem:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideKeyboard)];
// prevents the scroll view from swallowing up the touch event of child buttons
tapGesture.cancelsTouchesInView = NO;
[pageScrollView addGestureRecognizer:tapGesture];
[tapGesture release];
...
// method to hide keyboard when user taps on a scrollview
-(void)hideKeyboard
{
[myTextFieldInScrollView resignFirstResponder];
}
You can subclass your UIScrollView and override the method - hitTest:withEvent: which is called by the system to determine which view will handle the event. Whenever it is called, you can assume that a touch event occurred inside the scroll view, and by calling the super implementation, you can get the view which would normally process the event.
you can capture any kind of gestures in the UIscrollView. Make sure you also handle some of the default properties as well like set cancelsTouchesInView property to false, it is true by default. Also give some tag nos to your sub views to distinguish in selectors. & also enable their User interaction to true.
let tap = UITapGestureRecognizer(target: self, action:
selector(didTapByUser(_:)))

Detecting touches with stacked Views & passing Touch events from a UIScrollView

I'm trying to close a pop-up menu if the user selects anywhere EXCEPT with-in the bounds of the pop-up itself. In other words, they select the background, the pop-up closes.
I currently have a RootViewController that loads a UIView with a bunch of other UI Elements.
At one point, a pop-up (custom) menu is rendered - this is a separate UIViewController with associated .xib. Its loaded on the screen by:
SectionsChooserViewController *cv = [[SectionsChooserViewController alloc] initWithNibName:#"SectionsChooserViewController" bundle:[NSBundle mainBundle]];
cv.view.frame = CGRectMake(584, 391, 314, 346);
cv.delegate = self;
This view occupies only a small portion of the screen. So added detect code for touches to this view will only register the callback if you're ON the view.
If I don't handle touches in the "SectionsChooserViewController" and instead implement the touchesBegan in the rootViewController (which loads the "SectionsChooser...") it doesn't get called for 90% of the view because the UIScrollView's that I have seem to register the touch and not pass it on.
Is there a way to make the UIScrollView pass the touch event?
Edit: At least I think the UIScrollView's are taking the touch event. I'm not sure - I have nearly 20 UI Controls on the page.
Edit: Ok, it is the UIScrollView. If I turn off interaction-enabled on both scrollView's the UIView will get the touches, but this breaks the UIScrollViews - so that won't work.
You might be able to use [super touchesBegan:withEvent:] to help solve the problem.
Depending on what your trying to do you might be able to use UIGestureRecognizer to capture the touch events.
UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleDoubleTap:)];
doubleTapGestureRecognizer.numberOfTapsRequired = 2; //change value to 1 for single taps
[self addGestureRecognizer:doubleTapGestureRecognizer];

Resources