I'm creating a custom control* by subclassing UIControl and overriding the following methods:
- beginTrackingWithTouch:withEvent:
- continueTrackingWithTouch:withEvent:
- endTrackingWithTouch:withEvent:
- cancelTrackingWithEvent:
This control will support multitouch interaction. Specifically, one or two fingers may touch down, drag for some duration, and then touch up, all independently.
My issue comes at the end of these multitouch events, in endTrackingWithTouch:withEvent:. The system calls this method on my control exactly once per multitouch sequence, and it is called on the first touch up that occurs. This means that the second touch, which could still be dragging, will no longer be tracked.
This sequence of actions serves as an example for this phenomenon:
Finger A touches down. beginTracking is called.
Finger A drags around. continueTracking is called repeatedly to update my control.
Finger B touches down. beginTracking is called.
Fingers A and B drag around. continueTracking is called repeatedly to update my control.
Finger B touches up. endTracking is called.
Finger A drags around. No methods are called.
Finger A touches up. No methods are called.
The issue lies in the final three steps in this sequence. The documentation for UIControl says the following of endTrackingWithTouch:withEvent::
Sent to the control when the last touch for the given event completely ends, telling it to stop tracking.
Yet it seems to me that after finger B touches up in step 5, above, the last touch for the given event has not yet completely ended. That last touch is finger A!
*A range slider, if you must ask. But there are already plenty of open source range sliders, you say. Yes, true, but this one will support multitouch and Auto Layout.
I had the same issue in custom control with multitouch.
Because UIControl is subclass of UIView that inherits from UIResponder, so feel free to use:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
methods.
These methods work fine in UIControl subclass.
Related
When a user highlights a button in my horizontal scrollView, an identical looking button (Different draggable subclass) is added right above. What I'm trying to achieve is similar to lifting a card off a stack like Solitaire, and having another card underneath it.
Everything is working except that right now the moment the user highlights, the touch get's canceled as the new view come's into place. The user has to lift their finger off and tap again to register that the dragging event has begin. What I want to do is to automatically pass the touch from the first button to the newly added button without the user having to remove their finger again and touching the new button. I'm not even sure if this is possible.
Maybe I'm thinking about this the wrong way, but the reason I don't make the first button the draggable button is because I reuse it in my horizontal scrollView.
Which I feel you should consider each card as a custom UIView and handle
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
to show selection, dragging and finally placing of a card. So all logic for selection and put card would in one custom UIView class. For all other validation do that in your parent UIViewController
I have UITableView, and added the ability for moving cells.
http://s017.radikal.ru/i402/1503/76/3442a9517cec.png
So, if longPressGesture is recognized, i make the cell hidden, take a snapshot of cell(on picture highlighted with grey), and change it's position while longPressGestureStateChanged. But the animation of moving looks bad.
If I add panGesture while longPressGestureBegan, it doesn't work until I touch up and touch down again, and after that panGestureStateChanged begin working, and moving become smooth.
I need panGesture begin working while longPressGestureBegan, or catch the screen touch position.
But the difficulty is: (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; etc... doesn't work on UITableView.
I read a lot, but nothing was founded. This is the custom case. I need for example to recognize pinchGesture only when longPressGesture was recognized, without touching up.
Anybody know how to solve this?
For UITableView use ready methods to move cells. You can find more information in the documentation.
I'm wanting to be able to detect the following in Cocos2d v3:
A touch is initiated and held, then a second touch occurs somewhere else on the screen. Think of holding with one finger, and tapping with a second.
I've tried to use - (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event but this is only called the first time the second touch occurs, not subsequently.
To be clear, if I hold a touch on the screen and then tap somewhere else, the above method is called. But if I continue holding the first touch and then tap a second time, the above method is not called.
Also, touchBegan: is only called when the first touch occurs (i.e. the initial holding touch) and touchEnded: is only called when all touches are removed, including the initial holding touch.
I'd like to know:
1) How to recognise the above gesture in Cocos2d v3?
2) If 1) isn't possible, would there be a way to do it with my own gesture recogniser, and how would I implement my own gesture recogniser into Cocos2d v3?
Turns out by default Cocos2d V3 only responds to a single touch by default.
The solution:
self.multipleTouchEnabled = TRUE;
This means now every new touch will call:
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
And every time a finger is lifted from the screen it will call:
-(void) touchEnded:(UITouch *)touch withEvent:(UIEvent *)event
Even if there are other touches continuing.
if you use void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) then you get each touch count using pTouches->count();
I want to achieve a simple task - detect up to 10 touches in a UIView.
Using these:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
It all works great.
Problem is - touchesMoved:: is no more called - if first finger, that touched screen no longer touches screen.
Is it possible to fix it? (so that - while atleast one finger from 10 fingers is still touching screen - touchesMoved:: would be called?
If it is not possible in UIKit, could it be possible in Cocos2d, and how? (some links, function peaces would be really helpful)
You probably just need to set theView.multipleTouchEnabled.
I have a UIView subclass where I handle touches using touchesBegan, touchesMoved, touchesEnded.
I noticed that when I start a touch inside and then drag outside the UIView touchesEnded is not triggered, is there a way to have it called when I'm dragging outside the UIView frame?
Thanks
Use touchesCancelled instead.
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}
Pssibly it is calling the - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
Please check UIResponder Class Reference
touchesCancelled:withEvent:
Sent to the receiver when a system event (such as a low-memory
warning) cancels a touch event.
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
Parameters
touches
A set of UITouch instances that represent the touches for the ending phase of the event represented by event. event
An object representing the event to which the touches belong.
Discussion
This method is invoked when the Cocoa Touch framework receives a
system interruption requiring cancellation of the touch event; for
this, it generates a UITouch object with a phase of
UITouchPhaseCancel. The interruption is something that might cause the
application to be no longer active or the view to be removed from the
window
When an object receives a touchesCancelled:withEvent: message it
should clean up any state information that was established in its
touchesBegan:withEvent: implementation.
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 touchesCancelled: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. Availability
Available in iOS 2.0 and later.
You should still be getting touchesMoved in your viewController even you are outside of your view's frame. I would add array to your custom UIView which would keep touch objects that this view is assigned to and implement the following logic:
When you receive touchesBegan - add touch object to array for given UIView where touch coordinates match (you may have to iterate over all your subviews and match coordinates with frame to find the right one)
When you receive touchesMoved - remove touch from UIView's array for previous location and add it to UIView for current location if any
When you receive touchesEnded & touchesCancelled - remove touches from all UIViews arrays
When you remove or add touch object from your custom UIView array you may call your delegate methods (implement observer pattern) because at that point you know if it's really pressed or unpressed event. Keep in mind you may have many touch objects in one array so before calling your delegate check whether you added first object or deleted last one because that's the only case you should call your delegate.
I have implemented such solution in my last game, but instead of UIViews I had virtual buttons.