I have a problem as following, I've developed a ios app, to be concice , It has a UIViewController as parent, also it has a button , and the UIViewController popup a transparent UIView as a mask. When I click on the UIView(exactly within the underlying button boundary ) , the button could not receive any event(such as "touch up inside"), how could the button get the "touch up inside" event from transparent the UIView which is above the UIViewController?
This is not possible directly as the event triggered will not be for the button as button is not visible
(ie. another View is completly covering the button and is blocking the interaction with the user).
But i can give u a work around.
1.Declare a Bool Variable in your UIViewController
2.Implement the touches methods as shown below
- (void)touchesBegin:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
if(CGRectContainsPoint(button.frame, p) && !boolVariable) {
boolVariable = YES;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
// If the below condition is true then it mean there the user tapped on the same location as that of button..(touchesEnded and touchesCanceled was not called) so the event is just like touchUpInside
if(CGRectContainsPoint(button.frame, p) && boolVariable) {
boolVariable = NO;
[Here you can call the method which you wanted to call on touchUpInside of the button];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
- (void)touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
I was not able to test the able code on Xcode but i think it will work..
Note: The frame of the button should be with respect to the UIViewController.
Hope this helps u out :)
UIView instances, do not listen for touches. In order to get callbacks for events such as "touch up inside" are only sent to subclasses of UIControl
The most basic concrete subclass is UIButton.
Without code or more details about your app's setup, it's difficult to give better advice.
Related
I have a view being added as a subView of a viewController.
Both this subview and viewController all implement this method
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
I found that when I tapped the subView, both these method will be called.
I want to call subview's touchesEnded only. How to achieve this nicely? (not to add a gesture in it)
In the touchesEnded, apple doc says this "If you override this method without calling super (a common use pattern), you must also override the other methods for handling touch events, if only as stub (empty) implementations."
what's the other method ?
You are close!
To prevent to pass touch event to superview, you should override all the methods for the touch events. Add all the touch event methods to your subview, then you should be OK.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
Instead of implementing the subview's touchesDidEnd why not do the whole work in the superview's touchesDidEnd something like this
if([touch anyObject].view == subview){
return;
}
This way you will be able to know whether the touch was originally from the subview or the superview.
Alternative : You can implement both the methods in the superview and subview, but like the above code you can return the call in the superview, if the view it interacted with is the subview, and keep working in the touches code in the subview
If in case you want a different opinion plz let us know what you are trying so that we can give the answer accordingly
So, in my app, then a map annotation is pressed, It reveals a modal view controller. Then when an element that modal controller is pressed, I open another, smaller controller. In both controllers, I have implemented the method
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
if (!CGRectContainsPoint([self.view viewWithTag:21].frame, touchPoint)) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
Here is an image that shows my layout:
My problem is that when I touch in yellow circle one, the second modal dismisses, and after that, the first modal dismisses. SO, how can I prevent the first modal from receiving the touch.
PS: both modals are transparent UIViews, with smaller views inside them to display the content.
Obviously the event is passed through the view controller to the next responder.
In the class reference of UIResponder.
The default implementation of this method does nothing. However
immediate UIKit subclasses of UIResponder, particularly UIView,
forward the message up the responder chain. To forward the message to
the next responder, send the message to super (the superclass
implementation); do not send the message directly to the next
responder. For example,
[super touchesMoved:touches withEvent:event];
If you override this method without calling super (a common use
pattern), you must also override the other methods for handling touch
events, if only as stub (empty) implementations.
So if you don't want the event to forward to the next responder, you should override the other methods.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
if (!CGRectContainsPoint([self.view viewWithTag:21].frame, touchPoint)) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
I have a segmented control,with 5 segments..I want some event to be fired when a touch down happens on any of the segment and i mean tap and hold,without lifting the finger.
And when the user lifts his finger,it should reset
I have tried using touchesBegan and touchesEnded but i don't get the current selectedIndex in touchesBegan,here's my code
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
long oldValue = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (oldValue == self.selectedSegmentIndex )
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesEnded");
NSLog(#"selectedIndex%lu",self.selectedSegmentIndex);
[super touchesEnded:touches withEvent:event];
self.selectedSegmentIndex = -1;
}
Is there any way to achieve a touch down event effect for a segmentedControl,or any other alternative?
You can use touchDown: IBAction selector.
You can try to override:
- (void) setSelectedSegmentIndex:(NSInteger)toValue
Quite frankly, I do not think it is possible to do with UISegmentedControl. You do not get selected segment index until touchesEnded:withEvent: is called.
If you want to get in touchesBegan:withEvent:, my advise would be to write your own custom view that look like UISegmentedControl and gives you call back on touchesBegan:withEvent:.
As suggested by kientux above, adding transparent views or buttons on top of each segment in UISegmentedControl could be another potential solution to your problem but it would be very very tricky!
Good luck!
I am making a custom iOS keyboard and have a UIControl subclass to represent my button. I am trying to get the same behaviour as the normal iOS keyboard:
User begins touch on one button
User drags over other buttons (need to detect this so they can highlight/dehighlight accordingly)
Register the actual keyboard "press" when the user lifts their finger; that is, the touch ends
I am testing using the touch tracking methods like this:
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super beginTrackingWithTouch:touch withEvent:event];
NSLog(#"Begin for %#", [self label]);
return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super continueTrackingWithTouch:touch withEvent:event];
NSLog(#"Continue for %#", [self label]);
return YES;
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[super endTrackingWithTouch:touch withEvent:event];
NSLog(#"End for %#", [self label]);
}
These methods are all called, except they are only ever called on the UIControl where the touch began.
What is the best way to recognise touches coming and going across all my buttons? Do I have to do it all via the parent view?
I'll select a better answer if offered... but in case anybody finds this by search, I've managed to get what I need this way:
Set userInteractionEnabled to NO for the button class (UIControl subclass)
Don't override any touch methods in the button class
Implement touchesBegan:withEvent:, touchesMoved:withEvent: and touchesEnded:withEvent: in the view controller
On each event, extract the location from the UITouch object
Iterate over all of the button subviews and find the one containing the touch location:
- (MyButton *)buttonForTouch:(UITouch *)touch
{
CGPoint windowLocation = [touch locationInView:keyboardView];
for (MyButton *button in buttons) {
if (CGRectContainsPoint([button frame], windowLocation)) {
return button;
}
}
return nil;
}
Having determined which button the user is interacting with, make the view controller send messages to the relevant buttons to adjust their appearance
If appropriate, keep a reference to the UITouch instance in touchesBegan:withEvent: so you can be sure that you're tracking the same one in the other methods
I think that you should have a single big UIControl which has different subviews (like UIButton) but tracks touches by itself like you did already but finds out which subview to highlight depending on the touch position.
I want to enable some functionality that is triggered by clicking on the screen. the view of the screen is a UIWebView, and I'd like to be able to tell when the user clicks on it, though not when he clicks on a link inside of it, but any other place.
Any Ideas?
Tnx!
Have you looked at touchesBegan: / touchesEnded: in the UIResponder Class Reference
look at this code snippet. it might help you.
NSSet *touch;
CGPoint mTouchLocation;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touch = [event allTouches];
mTouchLocation = [[touches anyObject] locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesMoving");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchEnded");
CGPoint newTouchLocation = [[touches anyObject] locationInView:self.view];
}
touchesBegan is invoked when user touched the screen, touchesMoved when the user move the finger holding the screen and touchesEnded is when the touch is released.