I know I can use UIControlEventTouchDragEnter in order to tell when I have touched a button dragged my touch outside its bounds and then re-entered those bounds. However I was wondering would it be possible to touch the screen, not on a button, and detect when I have dragged over/inside that button?
Also could someone tell me the difference between UIControlEventTouchDragExit UIControlEventTouchDragOutside
Thanks!
You would have to observe touch events on the button's superview and whenever the user's fingers move, call hitTest:withEvent: to check whether the touch coordinates lie on top of the button.
I believe the difference between UIControlEventTouchDragExit and UIControlEventTouchDragOutside is this: when the finger moves from inside the control to the outside, UIControlEventTouchDragExit fires once. Then, as long as the finger remains outside, UIControlEventTouchDragOutside fires on each move. But you should test this yourself.
Related
What I want: Touch a button and a view is added right where the touch is. Without having to lift the finger the touches began/moved automatically begins working on the UIView. So without lifting the finger, I have touched the button and can drag the new view around.
What I don't know how to do:
Stop the touch events on the button and immediately send the touch events to the new view that is directly under the finger.
One option could be to ditch the button and just use the uiview touches to detect when to add the subview you want to let the user drag...
Daniel
As #Daniel suggested ditch the button and just use a UIView, but I believe you may need to have a UIPanGestureRecognizer in place to get your dragging.
You could set a flag when a new UIView is created and then forward any gesture events to that view - only while the user still has their finger down from the initial touch.
After the user has lifted their finger the new view can just deal with gestures by itself by adding a UIPanGestureRecognizer to it.
i want to do a custom toolbar, something like the slide to unlock of android phones. In idle state, the user can see a button of the bottom left of the page. the user would then tap it, drag towards the right. When the user reaches the right end, the toolbar will then 'lock'. Buttons would be located at the toolbar.
I'm think of using a customview and touchmoved functions, but what I don't know how is how to make the view move with the touch, and how to actually lock the bar.
Everytime you move your finger the touchmoved function is called. In the touchmoved function you have to redraw the whole view or just set a new frame for this view. It pretty simple as you already know how to detect touches and react on them.
I'm building an iOS board game (similar to scrabble or words) that involves moving around little tiles on the screen and I'm finding the user sometimes has a hard time touching and moving the tiles around because they're too small. Due to the design of the game, I can't increase the size of the tiles, so I've had to implement some little tricks that make touching and moving the tiles easier for the user and they work well. The only issue that remains is the user sometimes touches just barely outside the tile and when the user tries to move it, the tile stays where it is.
I have two ideas for how I can fix this...
If a tile isn't touched when the user touches the screen, I can use the parent view's touch location to find the closest tile to the touch location and somehow forward the touch event to that tile's view.
If a tile isn't touched when the user touches the screen, I can somehow "catch" the tile when the user drags their finger over it as they attempt to move it.
I'd prefer to implement solution #2 since solution #1 has too many problems associated with, not to mention solution #2 is a more realistic experience. That said, how can I "catch" a tile when the user drags their finger over it and send it touch events to move it where the finger is?
Currently, my tiles are implemented as subclasses of the UIView and they handle the touch events (touchesBegain, touchesMoved, and touchesEnded) directly. So if the user touches just barely outside the view and drags their finger over the view, it doesn't receive any of the touch events since it didn't receive the initial touchesBegan event.
Thanks in advance for all your wisdom!
Maybe you should have the "board" view handle all the dragging. When a touch begins and there is a tile at that point, then start dragging it. Otherwise check whenever the touch is moved and as soon as you find a tile, start dragging it.
You could override hitTest:withEvent: in the board view so that it can still detect when a touch hits a tile, but always return itself so that touch events go to the board view (e.g. record the subview that was hit in a separate member variable, so that you know what to start dragging later on when touch events start coming in).
More Details
When handling touches, UIView will use hitTest to find the view that should receive touch events. The default implementation checks each subview so that the "deepest" subview in the hierarchy gets the touches. In order for the board view to receive touches, you would have to disable userInteraction on all of the tile views. But that means you can't use hitTest to find the tile that was touched, since it ignores views that have userInteraction disabled.
So what I am saying is leave userInteraction enabled on the touch views. But override hitTest on the board view so that it first calls the super implementation in order to find a tile (if the result is self, the board itself was hit). No need to implement your own tile searching. Something like this:
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if ( hitView != self )
self.draggingTile = hitView;
return self;
}
Now you know what tile to move in touchesMoved. However, I don't think hitTest is called as the touch is moved, so if no tile has been picked up yet, you may have to call it manually (you can get the point and event from the touch passed to touchesMoved.
Have you looked into the UIGestureRecognizer API? I think your best option would be to add a UIPanGestureRecognizer recognizer to your board's view which would then fire back the selector to your UIViewController.
Setup in ViewDidLoad:
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePan:)];
[[self view] addGestureRecognizer:panGestureRecognizer];
And then implemented the selector:
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer{
CGPoint currentPoint = [gesture locationInView:[self view]];
}
When you set up the gesture recognizer you can set parameters to limit the callbacks (only recognize pans with 1 finger, etc. And in the callback you can check the gesture properties to see if the pan in continuing or if it's coming to an end. You can also grab the current point and determine if it's in or near a tile.
I'm using TouchesBegan TouchesMoved with Multitouch.
I have a manual implementation of what is essentially a button.
I bounds test on the point of TouchesBegan to set the button as down and the same for TouchesEnded to reset it.
The problem is if the user moves the finger out of the bounds of the button before lifting then the TouchesEnded is outside of the bounds of the button where the touch started.
I can't just reset everything on touchesended as the user might still be holding another button down with another finger.
What is the recommended solution to this? UIButton must be doing something similar somehow.
You need to watch touchesMoved: and "deactivate" your button when the touch moves outside of its bounds, and "reactivate" your button when the touch moves back in. See Handling a Complex Multi-Touch Sequences for explanation of how to watch for mutations on a multi-touch sequence (fancy way of saying "which finger was that?")
I want to switch between a couple views with a flick gesture using two fingers. If anyone can tell me what I need to do please help.
Without actually writing the code for you, here's what you'll need to do to track a multi-finger swipe:
First, set your view's multipleTouchEnabled property to YES so that you'll be able to track multiple touches.
In touchesBegan, store the each touches' locationInView property (this is a CGPoint).
Define a "swipe window" that limits the amount of off-axis motion you'll accept and still consider the gesture a swipe. If, for instance, you're looking to track horizontal stripes, perhaps you'd want a "swipe window" of 12x6 -- that is, if your touches move at least 12 horizontal pixels while moving fewer than 6 vertical pixels, you'll consider it a swipe.
In touchesMoved, compare the current location of the touches with the stored starting locations from step 2. If they're still in the "swipe window," do nothing. If one or both of the fingers has moved outside of its "swipe window," then cancel the swipe checking. If they've both met the requirements for a swipe, fire whatever method you want to have called when you've detected a multi-finger swipe.
In 'touchesEnded', stop tracking the swipe, since if the touches have ended but you still haven't fired the swipe method from #4, then they must not have constituted a swipe.
Hope this helps.