I have a UIButton that i'd like the user to be able to drag with TouchDragInside. How do i get the button to move as the user moves their finger?
As Jamie noted, a pan gesture recognizer is probably the way to go. The code would look something like what follows.
The button's view controller might add a gesture recognizer to the button (possibly in viewDidLoad) as follows:
UIPanGestureRecognizer *pangr = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[myButton addGestureRecognizer:pangr];
[pangr release];
And, the view controller would have the following target method to handle the gesture:
- (void)pan:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateChanged ||
recognizer.state == UIGestureRecognizerStateEnded) {
UIView *draggedButton = recognizer.view;
CGPoint translation = [recognizer translationInView:self.view];
CGRect newButtonFrame = draggedButton.frame;
newButtonFrame.origin.x += translation.x;
newButtonFrame.origin.y += translation.y;
draggedButton.frame = newButtonFrame;
[recognizer setTranslation:CGPointZero inView:self.view];
}
}
CORRECTED as per rohan-patel's comment.
In the previously posted code , the x and y coordinate's of the origin of the button's frame were set directly. It was incorrect as: draggedButton.frame.origin.x += translation.x. A view's frame can be changed, but the frame's components cannot be changed directly.
You probably don't want to use TouchDragInside. That is a method of recognizing that a button or other control has been activated in a certain way. To move the button, you probably want to use a UIPanGestureRecognizer and then change the buttons position in its superview as the users finger moves around.
You have to implement these four methods, touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, and touchesCancelled:withEvent: in the view that holds the button. The property you are referring to cannot be used directly to drag any uiview
Related
I'd like to detect swipe on the entire screen, however, the screen contains UIButtons, and if the user taps one of these buttons, I want the Touch Up Inside event to be triggered.
I've create a UIView on the top of my screen, and added a UIPanGestureRecognizer on it to detect the swipe, but now I need to pass the gesture through that view when I detect that it's a tap rather than a swipe.
I know how to differentiate the gestures, but I've no idea on how to pass it to the view below.
Can anyone help on that? Thanks!
Thanks for your answer. The link helped me to solve part of my problem.
I've set the buttons as subviews of my gestureRecognizer view and I can now start a swipe from one of the buttons (and continue to use the buttons as well). I managed to prevent the buttons to go to the "down" state by using the following code :
UIPanGestureRecognizer *swipe = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(swipeDetected:)];
swipe.maximumNumberOfTouches = 1;
swipe.delaysTouchesBegan =YES;
swipe.cancelsTouchesInView = YES;
[self.gestureRecognitionView addGestureRecognizer:swipe];
there is a BOOL property of UIGestureRecognizer cancelsTouchesInView. default is yes. set it to NO , and the touches will pass thru to the UIView
also have a look at the solution for this question
If you want to prevent the recognizer from receiving the touch at all, UIGestureRecognizerDelegate has a method gestureRecognizer:shouldReceiveTouch: you can use:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// don't override any other recognizers
if (gestureRecognizer != panRecognizer) {
return YES;
}
CGPoint touchedPoint = [touch locationInView:self.someButton];
return CGRectContainsPoint(self.someButton.bounds, touchedPoint) == NO;
}
I am building an iphone app that has multiple UIViews in a 3x3 matrix (so total of 9 UIViews) within one UIViewController. I am trying to find a way to let the user move one view to a location in the matrix which will then rearrange the rest of the views accordingly. Think of when you drag an app on your springboard to another place and all the other icons arrange themselves accordingly.
What is the best way to accomplish something like that?
Use UIPanGestureRecognizer, using the translationInView to adjust the coordinates of the item you're dragging. For a discussion of gesture recognizers, see the Event Handling Guide for iOS.
When you let go (i.e. the gesture ends), you can use UIView class method animateWithDuration to animate the moving of various items to their final locations.
So, in viewDidLoad, you might do something like the following (assuming that you had your nine controls in an array called arrayOfViews):
for (UIView *subview in self.arrayOfViews)
{
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(movePiece:)];
[subview addGestureRecognizer:gesture];
}
And then your gesture recognizer handler might look like:
- (void)movePiece:(UIPanGestureRecognizer *)gesture
{
static CGPoint originalCenter;
if (gesture.state == UIGestureRecognizerStateBegan)
{
originalCenter = gesture.view.center;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
CGPoint translation = [gesture translationInView:self.view];
gesture.view.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
[UIView animateWithDuration:0.25
animations:^{
// move your views to their final resting places here
}];
}
}
This is the bare-bones of what a dragging of controls around might look like.
If you are developing for iOS 6, definitely have a look at the UICollectionView class. If you have dealt with table views before the learning curve should not be too steep. You get the rearranging as well as all the animation for free.
I added a UIPanGestureRecognizerto my UIButton. And now my when I drag a short distance with my finger after I pressed the button, the button stops being highlighted. Normally, a UIButton will stay highlighted unless you drag pretty far outside the UIButtons frame. However, with a pan gesture recognizer, now even if I drag a little bit, the button stops being highlighted.
Nothing in my code sets the buttons highlighted property to no. I even tried erasing all the code in my panning gesture recognizers action selector thing(the method that gets called whenever i pan on my button).
I also tried setting the button's highlighted property to NO in the panning gesture recognizer's action selector thing.
This kind of worked its just the highlightedness flashes. When you pan the highlighted goes away then comes back really fast, like a flash. So, this doesnt work too. Any ideas?
Gestures, by default, will cancel the touches in the views that they are linked to. So, when the touches in your button get cancelled it becomes unhighlighted. To prevent this behavior, set the cancelsTouchesInView property of your gesture to NO.
This will work
-(void) panDetected:(UIGestureRecognizer *) gesture
{
if(gesture.state == UIGestureRecognizerStateBegan)
{
yourButton.backgroundImageView.image = [UIImage imageNamed:#"your_button_highlited_image.png"];
}
else if(gesture.state == UIGestureRecognizerStateChanged)
{
}
else if(gesture.state == UIGestureRecognizerStateEnded)
{
yourButton.backgroundImageView.image = [UIImage imageNamed:#"your_button_normal_image.png"];
}
}
I have experienced the same problem. borrrden is right; to fix it, set the viewController the delegate for the gestureRecognizer, and cancel the touch if the touch is on a IUButton. First add the protocol `UIGestureRecognizerDelegate on the .h file. Then, after creating the gesture recognizer, set the view controller as its delegate:
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(aMethod)];
gestureRecognizer.delegate = self;
[self.view addGestureRecognizer:gestureRecognizer];
[gestureRecognizer release];
Finally override shouldReceiveTouchand cancel unwanted touches:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if ([touch.view isKindOfClass:[UIButton class]]) { //Do not override UIButton touches
return NO;
}
return YES;
}
I've added a TapGestureRecognizer to my self.view:
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
[self.view addGestureRecognizer:tap];
[tap release];
The view contains a single UIScrollView with images and labels. I want to detect if the user taps on a label or not.
- (void)singleTap:(UIGestureRecognizer*)gestureRecognizer {
CGPoint pt = [gestureRecognizer locationInView:self.view];
UIView *v = [self.view hitTest:pt withEvent:nil];
if ([v isKindOfClass:[UILabel class]]) {
NSLog(#"label!");
return;
}
// else do other stuff if its not a label
However I don't see the label! in my log.
I think it's because userInteractionEnabled is by default NO on UILabels. Try turning that on.
EDIT: It was really a guess, but just to confirm, Apple docs on [UIView hitTest:withEvent:] state:
This method ignores view objects that are hidden, that have disabled user interaction, or have an alpha level less than 0.01.
Your subviews, such as the labels themself, actually hide the user interactions from the underlying view.
Why don't you add the gesture recognizers to your label(s).
Alternatively you may want to use UIButton for the labels.
Or -
if you do not want to determine which label has been touched, you may want to add an invisible view (an empty view, neither a hidden one nor one with alpha=0) on top of all labels and add the gesture recognizers to those.
I have an UIButton and I want to move that button by touching and swiping it in the screen. When I release the touch it will be in the current position. Explain clearly, please.
You can move a view by using touch moved event. There is a sample tutorial MoveMe by Apple which drags a view and after releasing the touch animate the view. Check specially the touch events (touchesBegan, touchesMoved, touchesEnded) in MoveMeView.m to get the idea how they have moved placardView. You can move your button just like the placardView.
Taken from 'drag' move a uibutton so it acts like uiScrollView
check this
you should make move frame from the points and move frame accordingly so that your button moves in places of touches
If you are scripting for iOS 3.2 and above, consider using UIPanGestureRecognizer.
Simply attach an instance of it like this,
...
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
panGesture.maximumNumberOfTouches = 1;
panGesture.minimumNumberOfTouches = 1;
[self.button addGestureRecognizer:panGesture];
[panGesture release];
...
and define handlePan: like this,
- (void)handlePan:(UIPanGestureRecognizer *)panGesture {
CGRect buttonFrame = self.button.frame;
CGPoint translation = [panGesture translationInView:panGesture.view];
buttonFrame.origin.x += translation.x;
buttonFrame.origin.y += translation.y;
[panGesture setTranslation:CGPointMake(0, 0) inView:panGesture.view];
self.button.frame = buttonFrame;
}