Quick question: how do i detect if a tap gesture recognizer is within a subview of the view it is added to? Eg. if i click on an object such as a square that has been added as a subview to a background which a tap gesture recognizer has been added to, how do I detect that it has been tapped?
You can grab the point of the tap off the gesture recognizer when your handler method is called respective to any view you wish using -locationInView:. Then, use the following method on UIView: - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event to get a reference to the actual sub view that was tapped remembering that the point you pass in is in the same coordinate space as the view.
Some code to get you started:
CGPoint point = [tapGestureRecognizer locationInView:parentView];
UIView *tappedView = [parentView hitTest:point withEvent:nil];
For hit testing to work the view needs to have the userInteractionEnabled property set to YES. Many views, such as UILabels have this set to NO by default. So prior to the above:
self.subviewOfInterest.userInteractionEnabled = YES;
maybe you should set as:
subviews.userInteractionEnabled = YES;
good luck!
you can use the requireGestureRecognizerToFail: to recognize the tap on subview please refer this code
Related
I have an iPad project structured with a UISplitViewController:
RootViewController
DetailviewController
Both of them are detecting touches with Gesture Recognizer inside their own Class.
I would like to create a transparent UIView on top of all the Classes to detect ONLY a Diagonal Swipe (from the left bottom corner to the right top corner).
So, when the swipe will be detected I will launch a function otherwise nothing appended and the touch should be passed on the low level view.
I tried these two solutions:
Add a GestureRecognizer on this top transparent view but this will hide all touches to the lower hierarchy views.( with userInteraction enabled: YES ofcourse);
The other solution is to make the init like this
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setBackgroundColor:[UIColor colorWithWhite:1 alpha:0.01]];
[self setUserInteractionEnabled:NO];
}
return self;
}
and try to detect the swipe with
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
But at this point all the touches are not detected.
Anybody have a nice solution?
I will not create a transparent UIView like you are mentioning. I will add a UISwipeGestureRecognizer to the UISplitViewController's view this is already the view that contains all your subviews. You can have access to the view within the app delegate:
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
// attach the swipe gesture to the view that embeds the rootView and the detailView
UISwipeGestureRecognizer* swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:splitViewController.view action:#selector(swipeUpdated:)];
Can't you just add a gesture recognizer to the UISplitViewController's view?
You should look into Container Controllers. You can make your own SplitViewController and make a third view on top of the controller that detects the swipe. Custom container controllers are pretty straight forward and gives you a lot of flexibility.
I have a table with static cells. One of these cells has a view in it with a pan gesture recogniser on it.
When I am scrolling down my tableview, when I get to the cell with the view with pan gesture recogniser, scrolling doesn't seem to work. If I touch outside the view (to the side or top or bottom) it works and I can scroll. I have an if statement in my gesturerecognizer that tests whether a certain area has been touched, and if so performs an action.
I have looked at this issue (http://stackoverflow.com/questions/3295239/uigesturerecognizer-blocking-table-view-scrolling) but setting cancelsTouchesInView to NO didn't work, I don't have anywhere setting the state property and using the method - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
I don't know where to get the 'otherGestureRecognizer' from or what object to call that method on.
I'm assuming I wan't to put my gesture recogniser as the first argument, and the tableview's scroll gesture recogniser as the otherGestureRecogniser, is that correct? If so, how do I get that?
UIPanGestureRecognizer *windPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveWindHandle:)];
[self.windRangeView addGestureRecognizer:windPanGesture];
Then in my moveWindHandle:
-(void)moveWindHandle:(UIPanGestureRecognizer *)gesture
{
gesture.cancelsTouchesInView = NO;
isMovingHandle = [self isPoint:startedTouchAt insideHandle:_toHandleWindImageView];
if(isMovingHandle) {
if(gesture.state == UIGestureRecognizerStateBegan) {
//do stuff
}
}
else
{
//i want it to ignore this gesture and just scroll like normal if that is what hte user did
}
}
I have set the tableviewcontroller as a UIGestureRecognizerDelegate, but I don't know what to do with that.
You would not be the one calling -gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:. That method is called by the system. You need to set your table view controller as the delegate for your window pan gesture.
windPanGesture.delegate = self;
At that point, when you do the pan, the system will call the delegate method -gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: with your gesture recognizer as one argument and the scroll view's gesture recognizer as the other.
Update
You may also want to implement the -gestureRecognizerShouldBegin: method and return NO if you are not in one of the certain areas.
I have a question that I've searched but can't find a definative answer to. Here is my layout:
UIView - ViewController
|_UIScrollView - added programatically
| |_UIView to hold a backgound/perimeter - added programmatically
|_UIView 1 - added programmatically
|_UIView 2 - added programmatically
and so on
My question is how come the ViewController calls "touchesMoved" only once when I move say UIView 2 on touch?
Now UIView has it's own touchesMoved method, but I need the controller's touchesMoved to get called as I need it to talk to the ScrollView to update its position. Such as when that UIView 2 is near the corner, so that the ScrollView moves a little to fully show UIView 2.
If there is no way around this is there a way to update ScrollView from UIView 2 to scroll when its near a corner?
Edit:
I think I may have found a work around. Not sure if this will be accepted by Apple but:
I just made a call to a instance variable that is = to self.superview which then allows me to talk back to my ScrollView within UIView's touchesMoved
in that i can call the method [ScrollView setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated] so my ScrollView gets updated as the subview(UIView2) moves close to the edge of the UIWindow.
Thank you for the suggestions.
The behavior you describe is the result of the UIScrollView hijacking the touch moved event. In other words, as soon as the UIScrollView detect that a touch moved event falls within its frame, it takes control of it. I experienced the same behavior while trying so create a special swipe handler, and it failed each time a UIScrollView was also interested by the swipe.
In my case, I solved the issue by intercepting the event in sendEvent: overridden in my custom UIWindow, but I don't know if you want to do the same. In any case, this is what worked for me:
- (void)sendEvent:(UIEvent*)event {
NSSet* allTouches = [event allTouches];
UITouch* touch = [allTouches anyObject];
UIView* touchView = [touch view];
//-- UIScrollViews will make touchView be nil after a few UITouchPhaseMoved events;
//-- by storing the initialView getting the touch, we can overcome this problem
if (!touchView && _initialView && touch.phase != UITouchPhaseBegan)
touchView = _initialView;
//-- do your own management of the event
//-- let the event propagate if you want also the default event management
[super sendEvent:event];
}
An alternative approach that you might investigate is attaching a gesture recognizer to your views -- they have a pretty high priority, so maybe the UIScrollView will not mess with them and it might work better for you.
If there is no way around this is there a way to update ScrollView from UIView 2 to scroll when its near a corner?
Have you tried to make the UIScrollView scroll by calling:
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
in my app, when the user touches on the view ,i am showing an UIImageView there and i drag and
drop the image from another UIImageView to that touched UIImageView.
But the problem is that, only the recent touched UIImageView is activated. i mean ,when i
click 3 times then shows 3 UIImageViews but only the last is activated and accept the another
image.
How can i make all touched UIImageViews are activated .. Any body help on this..
Thanks in advance.
You should read Apples documentation on the responder chain and event handling. The key UIView method here is
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
This method traverses the view hierarchy by sending the
pointInside:withEvent: message to each subview to determine which
subview should receive a touch event. If pointInside:withEvent:
returns YES, then the subview’s hierarchy is traversed; otherwise, its
branch of the view hierarchy is ignored. You rarely need to call this
method yourself, but you might override it to hide touch events from
subviews.
You should try this:
Where there is your UIView (Or a view):
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapped:)];
tapGesture.numberOfTapsRequired = 1;//number of tap
[view addGestureRecognizer:tapGesture];
The selector:
-(void)tapped:(UITapGestureRecognizer *)sender {
NSLog(#"Pressed");
}
I'm having some class which is a subclass of UITableViewController.
on one of the TableView's cell I'm adding a view that holds a UIButton as subview
(i.e. [someParentView addsubview:button])
I'm adding the view to cell like this:
[cell.contentView addSubview:someParentView]
I've set the UserInteractionEnabled both for the button and for the view holding it ("someParentView") to YES
but still when I tap it, nothing seems to be happening.
any idea?
Thanks
What i want to accomplish in short: i want to make a tableview that shows some views.
each view contains some subviews, and in one of those cases there is a uibutton as a subview.
i want to have this button to trigger event by user's tap, as any other uibutton, so some method will be launched.
to do that, i made a class which subclasses UITableViewController, and for each cell i added a view using [cell.contentView someView], as i wrote. i disabled selection from all using the [self.tableview setAllowSelection:NO] and for each sell made selection style as NONE.
as said, i also set the view and the uibutton UserInteractionEnabled property to YES.
anything i'm missing?
Been solved!
I have used the method:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
of UIView class in my subclass and returned the button as the returned object.
(of course, needed to test point if in button area).