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;
}
Related
Hi I have some buttons that trigger action methods.
In addition, I would like the user to be able to swipe left or right over the area with the buttons to show some different buttons
My idea was to add a UIView with a clear background color on top of the buttons so that the user could still see the buttons but could also swipe to the right or left.
However, when the view is over the buttons the buttons no longer work and when it is under the buttons, the swipe is not detected.
In addition to changing the view color to clear, I also tried adjusting the alpha however this seems to work the same way. When alpha of the UIView is zero, swipes are no longer detected and when alpha is greater than zero, the buttons no longer work.
Can anyone suggest a way to do this?
Thanks in advance for any suggestions.
Code I am using for buttons and to detect swipes seems standard. Trying to figure out a way to expose view and buttons at the same time.
Action methods to which buttons are wired:
//.h file
- (IBAction)showIncoming:(id)sender;
- (IBAction)showOutcoming:(id)sender;
/.m file
- (IBAction)showIncoming:(id)sender {
_showIncoming=YES;
_fetchedResultsController=nil;
[self.tableView reloadData];
[self updateInterface];
}
- (IBAction)showOutgoing:(id)sender {
_showIncoming=NO;
_fetchedResultsController=nil;
[self.tableView reloadData];
[self updateInterface];
}
Swipes:
//in viewWillAppear
UISwipeGestureRecognizer *rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(rightSwipeHandle:)];
rightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[rightRecognizer setNumberOfTouchesRequired:1];
rightRecognizer.delegate = self;
[_topView addGestureRecognizer:rightRecognizer];
[_topView setUserInteractionEnabled:YES];
// [rightRecognizer release];
//........towards left Gesture recogniser for swiping.....//
UISwipeGestureRecognizer *leftRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(leftSwipeHandle:)];
leftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[leftRecognizer setNumberOfTouchesRequired:1];
leftRecognizer.delegate = self;
[_topView addGestureRecognizer:leftRecognizer];
[_topView setUserInteractionEnabled:YES];
}
- (void)rightSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer
{
//Do moving
NSLog(#"Right Swipe performed");
_showOld=YES;
[self updateInterface];
}
- (void)leftSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer
{
// do moving
NSLog(#"Left Swipe performed");
_showOld=NO;
[self updateInterface];
}
The approach I suggest is listening to a user moving a finger events and firing specified method that would check if user's finger is on your button's view. Sample code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchedViewWithTouches:touches andEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchedViewWithTouches:touches andEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchedViewWithTouches:touches andEvent:event];
}
- (void)didTouchedButton:(NSSet *)touches andEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
UIView *touchedView;
for (UIView *view in self.subviews) {
if(CGRectContainsPoint(view.frame, touchLocation)) {
touchedView = view;
break;
}
}
if (view == self.showIncomingButton) {
[self showIncoming:self.showIncomingButton];
} else if (view == self.showOutcomingButton) {
[self howOutcoming:self.showOutcomingButton];
}
}
Note that you will have to add two outlets for your buttons (if you don't have any). Also, you can get rid off (id)sender in your IBAction calls.
I have UIBUtton in UICollectionViewCell as show in image. I had set touchUpInside Action in xib. I had set background image of button is pink and lable as yellow. UIButton comes at top of the label. Now the problem is that UIButton in not getting touch event in pink area it only get touch events on orange area. why it is so.
- (IBAction)checkButtonTap:(id)sender event:(id)event
{
DLog(#"%s",__func__);
}
O a hidden view is overlapping the button due to which it is touch event was not reaching to the button.
Just to be sure I understand your issue, you have your UICollectionView in yellow and your UIbutton in pink, correct ?
If so, it seems you want to intercept the touchUpInside event outside your button's superview in yellow. You can look at this answer which deal with kind of problem.
Even if you finally find the solution to your problem, to clarify my answer, if you want to interact with an UIButton which is not inside its superview's frame, you may need to need to implement the method - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event of the superview (here the UICollectionViewCell):
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (!self.clipsToBounds && !self.hidden && self.alpha > 0) {
for (UIView *subview in self.subviews.reverseObjectEnumerator) {
CGPoint subPoint = [subview convertPoint:point fromView:self];
UIView *result = [subview hitTest:subPoint withEvent:event];
if (result != nil) {
return result;
}
}
}
return nil;
}
(thanks Noam!)
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
[buttonname addGestureRecognizer:tapGesture];
buttonname.userInteractionEnabled = YES;
(IBAction)imageTapped:(UITapGestureRecognizer *)gestureRecognizer {
if(gestureRecognizer.state == UIGestureRecognizerStateRecognized) {
[buttonname becomeFirstResponder];
}
}
O sorry, there was hidden view that is overlapping the button due to which touch event was not reaching to the button.
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.
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..
Lets say I wan't to add 1 to an integer. This will only be done when I push down on a UIButton and then release my finger on another UIButton. A drag combo. Whats the easiest way I can make an IBAction occur out of a combo? This could be done with touch coordinates or maybe just UIButtons and IBActions.
How do I create a 2-button combo with IBActions
Try implementing the button you wish to touch down on as a "Touch Down", "Touch Up Inside", and "Touch Up Outside" button.
UIButtons can respond to many differing types of events
Touch Cancel
Touch Down
Touch Down Repeat
Touch Drag Enter
Touch Drag Exit
Touch Drag Inside
Touch Drag Outside
Touch Up Inside
Touch Up Outside
You can implement different action code for each of these for each button for best control of whatever you wish. The simplistic case only uses the 2 I mentioned above.
THIS CODE HAS BEEN TESTED AND DOES WORK
In your ViewController header file (here was mine):
#interface ViewController : UIViewController{
IBOutlet UIButton * upButton; // count up when finger released button
IBOutlet UIButton * downButton;
IBOutlet UILable * score;
BOOL isButtonDown;
unsigned int youCounter;
}
-(IBAction)downButtonDown:(id)sender;
-(IBAction)downButtonUpInside:(id)sender;
-(IBAction)downButtonDragOutside:(id)sender event:(UIEvent *)event;
-(IBAction)downButtonUpOutside:(id)sender event:(UIEvent *)event;
#end
In your .xib, connect the down button (the one you wish to be your original finger pressed button) to the proper actions above.
In your ViewController.m file
-(void)viewDidLoad{
[super viewDidLoad];
isButtonDown = NO;
youCounter = 0;
}
-(IBAction)downButtonDown:(id)sender{
isButtonDown = YES;
}
-(IBAction)downButtonUpInside:(id)sender{
isButtonDown = NO;
}
-(IBAction)downButtonDragOutside:(id)sender event:(UIEvent *)event{
NSArray theTouches = [[event allTouches] allObjects];
[downButton setHighlighted:YES];
if(YES == [upButton pointInside:[[theTouches objectAtIndex:0] locationInView:upButton] withEvent:event]){
[upButton setHighlighted:YES];
}else{
[upButton setHighlighted:NO];
}
}
-(IBAction)downButtonUpOutside:(id)sender event:(UIEvent *)event{
if(YES == [upButton pointInside:[[theTouches objectAtIndex:0] locationInView:upButton] withEvent:event]){
youCounter++;
score.text = [NSString stringWithFormat:#"Score = %d", youCounter];
}
[downButton setHighlighted:NO];
[upButton setHighlighted:NO];
}
//Initalize a BOOL variable to know if you started the touch in the right place.
BOOL correctStart = NO;
//Get the location of the first touch, if its in the first button make correctStart = YES.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
for (UITouch *touch in allTouches) {
if ([touch locationInView:button1.view]) {
correctStart = YES;
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
for (UITouch *touch in allTouches) {
if (([touch locationInView:button2.view]) && (correctStart == YES)) {
anInteger++;
}
}
correctStart = NO;
}
I did not try this code as I am not at my mac, so your results may vary, but this should get you going in the right direction.
Another approach is to use a UITapGestureRecognizer and UIPanGestureRecognizer and track the location in your buttons. I found this to be more readable. (I actually ended up using UILabels because I didn't need any further UIButton behavior.)
// Create a button, add targets
UIButton *cloudButton = [UIButton buttonWithType:UIButtonTypeCustom];
[cloudButton setImage:[UIImage imageNamed:#"notification_add.png"] forState:UIControlStateNormal];
[cloudButton setFrame:CGRectMake(0, 0, 30, 30)];
[cloudButton addTarget:self action:#selector(dragBegan:withEvent: )
forControlEvents: UIControlEventTouchDown];
[cloudButton addTarget:self action:#selector(dragMoving:withEvent: )
forControlEvents: UIControlEventTouchDragInside | UIControlEventTouchDragOutside];
[cloudButton addTarget:self action:#selector(dragEnded:withEvent: )
forControlEvents: UIControlEventTouchUpInside |
UIControlEventTouchUpOutside];
[self.view addSubview:cloudButton];
//event methods
- (void) dragBegan: (UIControl *) c withEvent:ev
{
NSLog(#"dragBegan......");
}
- (void) dragMoving: (UIControl *) c withEvent:ev
{
NSLog(#"dragMoving..............");
}
- (void) dragEnded: (UIControl *) c withEvent:ev
{
NSLog(#"dragEnded..............");
}
Do the following for the first UIButton in the ViewDidLoad method
[button1 addTarget:self action:#selector(dragMoving:withEvent: )
forControlEvents: UIControlEventTouchDragInside | UIControlEventTouchDragOutside];
[button1 addTarget:self action:#selector(dragEnded:withEvent: )
forControlEvents: UIControlEventTouchUpInside |
UIControlEventTouchUpOutside];
In the dragMoving method
- (void)dragMoving:(UIControl *)c withEvent:event{
NSSet* touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:[button2 superview]];
if ( CGRectContainsPoint(button2.frame, currentPoint) ) {
NSLog(#"over second button);
}
}
As for the dragEnded method:
- (void) dragEnded:(UIControl *)c withEvent:event{
NSSet* touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:[self.button1 superview]];
if ( CGRectContainsPoint(button2.frame, currentPoint) ) {
NSLog(#"ended on button 2");
//add 1 to the integer
}
else {
NSLog(#"not on button 2");
}
}