When I touch a UIButton, the touchesBegan, touchesMoved and touchesEnded methods are not called. I want to perform an event when a continuous touch happens and cancel the event when the touch ends.
You can track UIButton touch start and touch end by using UIButton control events.
- (void)viewDidLoad {
[super viewDidLoad];
[self.button addTarget:self action:#selector(touchStarted:) forControlEvents:UIControlEventTouchDown];
[self.button addTarget:self action:#selector(touchEnded:) forControlEvents:UIControlEventTouchUpInside];
[self.button addTarget:self action:#selector(touchEnded:) forControlEvents:UIControlEventTouchUpOutside];
}
- (void)touchStarted:(id)sender {
NSLog(#"Touch started");
}
- (void)touchEnded:(id)sender {
NSLog(#"Touch ended");
}
Related
I believe I have an issue with UITapGestureRecognizer for dismissing keyboard when tapping in chatroom area preventing or blocking touch to the previewCancelButton. Here below are my relevant codes:
BaseTemplateVC.m
- (void)addDismissKeyboardGesture {
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard:)];
tapGesture.cancelsTouchesInView = NO;
tapGesture.delegate = self;
self.view.tag = 111;
[self.view addGestureRecognizer:tapGesture];
}
- (void) dismissKeyboard:(id)sender {
UITapGestureRecognizer *gesture = sender;
UIView *view = gesture.view;
NSLog(#"%ld", (long)view.tag);
[self.view endEditing:YES];
}
ChatroomVC.m
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
//Disallow recognition of tap gestures in the segmented control.
if (([touch.view isKindOfClass:[UIButton class]])) {
NSLog(#"noooooooo");
return NO;
}
return YES;
NSLog(#"yesssssss");
}
InputFunctionView.m
- (void)selectedSticker:(NSString *)stickerURLString {
/* Sticker preview subview */
stickerPreviewView = [[UIImageView alloc] initWithFrame:CGRectMake(0, -120, FrameWidth, 120)];
stickerPreviewView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5f];
stickerPreviewView.userInteractionEnabled = YES;
[self addSubview:stickerPreviewView];
[self bringSubviewToFront:stickerPreviewView];
/* Initialise previewCancelButton */
self.previewCancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.previewCancelButton.frame = CGRectMake(Main_Screen_Width-30, SpaceForItems-120, 20, 20);
[self.previewCancelButton setBackgroundImage:[UIImage imageNamed:#"btn_previewcancel.png"] forState:UIControlStateNormal];
[self.previewCancelButton setBackgroundImage:[UIImage imageNamed:#"btn_previewcancel.png"] forState:UIControlStateHighlighted];
[self.previewCancelButton addTarget:self action:#selector(cancelStickerPreviewButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview: self.previewCancelButton];
}
/* Cancel sticker preview subview */
- (void)cancelStickerPreviewButtonPressed:(id)sender {
NSLog(#"cancel sticker preview");
[self.previewCancelButton removeFromSuperview];
[stickerPreviewView removeFromSuperview];
}
Now the previewCancelButton is correctly on right top corner of stickerPreviewView but unable to receive touch event to it. When I touch the button it shows "111" in the console and when I traced back I found BaseTemplateVC.m that contains addDismissKeyboardGesture method, so I believe this may cause the issue.
Anyone can guide me to some solutions. That'd be really appreciated. Thanks in advance.
Progress: I have modified gestureRecognizer method in ChatroomVC.m so now it can ignore tap gesture on the button but the problem remains action for the button doesn't get fired.
Just take a try with this, i guess it will work.
Use the shouldReceiveTouch delegate method of gesture, and return NO when the touch.view is button class.
So when it finds button It will discard the gesture and take button action.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Block the recognition of tap gestures in the button.
if (([touch.view isKindOfClass:[UIButton class]])) {
return NO;
}
return YES;
}
Here is the demo implementation :
I have taken the button on main view of view controller in the story board.
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureClicked:)];
tapGesture.delegate = self;
[self.view addGestureRecognizer:tapGesture];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures in the segmented control.
if (([touch.view isKindOfClass:[UIButton class]])) {
return NO;
}
return YES;
}
- (IBAction)btnTestClicked:(UIButton *)sender {
NSLog(#"test button click");
}
- (void)tapGestureClicked:(UIGestureRecognizer *)recog
{
NSLog(#"tap gesture clicked");
}
Hope it helps.
Happy coding ...
I found a solution to this by using below code in GestureRecogniser delegate method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isDescendantOfView:IFView.stickerPreviewView]) {
return NO;
}
return YES;
}
It specifies exactly what subview in this case IFView.stickerPreviewView is to return NO. Also in InputFunctionView, use this instead to add the subview:
[self.superview addSubview:_stickerPreviewView];
I wrote a simple demo to show my issue.The button need to be added two touch events:drag and touch.
UIButton * btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
btn1.frame = CGRectMake(300, 100, 100, 100);
btn1.backgroundColor = [UIColor redColor];
[btn1 addTarget:self action:#selector(stretchBtnDragMoving:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[btn1 addTarget:self action:#selector(stretchBtnDragEnded:withEvent:) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchUpOutside];
[self.view addSubview:btn1];
And then,
- (void)stretchBtnDragMoving:(UIControl *)control withEvent:(UIEvent *)event
{
NSLog(#"moving");
}
- (void)stretchBtnDragEnded:(UIControl *)control withEvent:(UIEvent *)event
{
NSLog(#"end or in");
}
When I touched the button in iPhone6s/6s plus like normal time,both of the methods be called.
But when I touched the button very light, only the second method be called.
When I used iPhone6/6 plus,iPad and other devices,however I touched the button,only the second method be called.
I usually use this method to implement the UIButton movable;
[button addTarget:self action:#selector(dragMoving:withEvent:) forControlEvents:UIControlEventTouchDragInside];
But it will trigger the touchUpInside event at same time, and I need touchUpInside event to do some other things.
So does anyone know how to avoid this?
Thanks a lot;
use these code below to solve it;
[button addTarget:self action:#selector(touchDown:withEvent:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:#selector(touchDragInside:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[button addTarget:self action:#selector(touchUpInside:withEvent:) forControlEvents:UIControlEventTouchUpInside];
- (void) touchUpInside :(UIControl*)c withEvent:ev{
NSLog(#"touch Up inside");
if (count) {
NSLog(#"here I can do something about clicking event");
}
}
- (void) touchDown:(UIControl*)c withEvent:ev{
NSLog(#"touch Down");
count = YES;
}
-(void) touchDragInside:(UIControl*)c withEvent:ev{
NSLog(#"touch drag inside");
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
count = NO;
}
Actually the best way to accomplish this is to use UIPanGestureRecognizer and add it to the button. It handles the dragging very well and you also can tap on it without needing to do anything else.
Say I have 2 UIButtons next to each other and both of them will turn from white to black when I put my finger on them.
That obviously works fine, but if I put my finger on button 1 it turns black and then when I move my finger over the button 2 without lifting my finger, button 2 doesn't turn black, button 1 is still black.
So how can I "swipe" over one button and then over another one and change the highlighted button from the first one to the second one.
In your viewcontroller override the touchesMoved event. This will tell you when touches occur. The problem is that your buttons will have to have userInteractionEnabled set to NO so that they don't gobble the touch event. This will be ok if you're trying to do a custom slider or something where you don't need the actual button events.
Then in the touchesMoved you can loop through your buttons and see which button is pressed. Here is some code stolen from an excellent answer at Highlight the button when drag enter
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *t in touches) {
CGPoint touchPoint = [t locationInView:self.view];
CGPoint testPoint = [self.view convertPoint:touchPoint toView:aButton];
if ([aButton pointInside:testPoint withEvent:event]) {
//Do something
}
//rest of code
[self.button1 addTarget:self
action:#selector(dragEnter:)
forControlEvents:UIControlEventTouchDragEnter];
[self.button2 addTarget:self
action:#selector(dragEnter:)
forControlEvents:UIControlEventTouchDragEnter];
[self.button1 addTarget:self
action:#selector(dragExit:)
forControlEvents:UIControlEventTouchDragExit];
[self.button2 addTarget:self
action:#selector(dragExit:)
forControlEvents:UIControlEventTouchDragExit];
- (void)dragEnter:(id)sender
{
UIButton *button = (UIButton *)sender;
button.highlighted = YES;
}
- (void)dragExit:(id)sender
{
UIButton *button = (UIButton *)sender;
button.highlighted = NO;
}
I'm trying to allow some UIButton instances on one of my views to be touched and dragged around the screen (eventually with momentum, but that's for later!). I have this working in a very simple form, shown below, but the problem is that by touching the button to begin dragging it, it attaches to the finger, and by lifting the finger off, the "Touch Up Inside" event is triggered, which is the code I want to execute when actually tapping the button.
In a nutshell: how do I differentiate between a tap, and a drag/release? Do I need to change the tap to a short-tap gesture recognizer, or similar, perhaps? Code:
In viewDidLoad:
[firstButton addTarget: self action: #selector(wasDragged: withEvent:) forControlEvents: UIControlEventTouchDragInside];
And my wasDragged method:
- (void)wasDragged:(UIButton *)button withEvent:(UIEvent *)event
{
if (button == letter1Button) {
UITouch *touch = [[event touchesForView:button] anyObject];
CGPoint previousLocation = [touch previousLocationInView:button];
CGPoint location = [touch locationInView:button];
CGFloat delta_x = location.x - previousLocation.x;
CGFloat delta_y = location.y - previousLocation.y;
button.center = CGPointMake(button.center.x + delta_x, button.center.y + delta_y);
}
}
You could use a UIPanGestureRecognizer and tell it to cancel touches in view...
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panRecognizer;
panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(wasDragged:)];
// cancel touches so that touchUpInside touches are ignored
panRecognizer.cancelsTouchesInView = YES;
[[self draggableButton] addGestureRecognizer:panRecognizer];
}
- (void)wasDragged:(UIPanGestureRecognizer *)recognizer {
UIButton *button = (UIButton *)recognizer.view;
CGPoint translation = [recognizer translationInView:button];
button.center = CGPointMake(button.center.x + translation.x, button.center.y + translation.y);
[recognizer setTranslation:CGPointZero inView:button];
}
- (IBAction)buttonWasTapped:(id)sender {
NSLog(#"%s - button tapped",__FUNCTION__);
}
For beginners like me, I tried UIPanGestureRecognizer as suggested above, but it did not work. So, here is my simple solution:
First, add event listeners as suggested by Baig:
// add drag listener
[button addTarget:self action:#selector(wasDragged:withEvent:) forControlEvents:UIControlEventTouchDragInside];
// add tap listener
[button addTarget:self action:#selector(wasTapped:) forControlEvents:UIControlEventTouchUpInside];
Both drag and tap will both trigger UIControlEventTouchUpInside, so add a flag in wasDragged:withEvent: like this:
-(IBAction)wasDragged: (id)sender withEvent: (UIEvent *) event {
was_dragged = YES;
UIButton *selected = (UIButton *)sender;
selected.center = [[[event allTouches] anyObject] locationInView:self.view];
}
- (IBAction)buttonWasTapped:(id)sender {
if(!was_dragged)
NSLog(#"button tapped");
else{
was_dragged = NO;
NSLog(#"button dragged");
}
}
Voila. Done.
Use UIControlEventTouchDragInside and UIControlEventTouchUpInside events of UIButton like
// add drag listener
[button addTarget:self action:#selector(wasDragged:withEvent:) forControlEvents:UIControlEventTouchDragInside];
// add tap listener
[button addTarget:self action:#selector(wasTapped:) forControlEvents:UIControlEventTouchUpInside];
You can use HBDraggableButton control..