I have a UIButton (a subclass of one, actually) that interacts with the user via the touchesbegan: and touchesmoved: functions.
What I would like is for the user to be able to press down the button, drag their finger away, and have a second finger touch the button (all while the first finger has never left the screen).
Problem is, the second touch event never calls touchesbegan: unless the first finger has been released.
Is there some way to override this, or am I trying to do the impossible?
Have you tried setting multipleTouchesEnabled to YES?
If the interactions are using touchesbegan: and touchesmoved: then use a UIView instead of a UIButton. A button is a UIControl, and the way to interact with UIControls is
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents.
I'm not sure this two ways of getting events mix well.
Related
I want to response this kind of touch event on a view:begin to touch down outside of the view and drag enter it. I have tried to use the iOS UIControlEvent such as UIControlEventTouchDragEnter,UIControlEventTouchDragInside and the UIGesture, and I found no way can do this directly.
Finally, I implement it with my own way:Create a custom subclass of UIView and overwrite the method (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event and forward touch event up to the responder chain. In the touchesBegan, touchesMoved, touchesEnded method of parent view, I use the location of UITouch object to judge touch down outside of the view and drag enter it.
I am not satisfied with this way. Is there anyone can tell me a more efficient and elegant way to work out it? Thank you very much for your time and consideration.
begin to touch down outside of the view and drag enter it
You can never do this more "elegantly" because if your initial touch down is outside the view, then it is not associated with this view and never will be. Whatever view the touch's initial hit test associates it with, that is the view that will always be this touch's view throughout the gesture, and touch events will be sent only to that view. The default definition of hit testing is that the view that the initial touch is inside is the hit-test view, and that, by hypothesis, is not your view.
I'm working on an app where the user is expected to rapidly touch and swipe across multiple UIViews, each of which is supposed to do an action once the user's finger has reached it. I've got a lot of views and so the typical thing to do, where I'd iterate over each view to see if a touch is inside of its bounds, is a no-go - there's just too much lag. Is there any other way to get touch events from one view to another (that is beside the first one)? I thought maybe there is some way to cancel the touch event, but I've searched and so far have come up empty.
One of the big problems I have is that if I implement my touch handling in my view controller, touchesBegan only fires for the first touch - if the user touches something and then, without moving the first finger, taps on something else, that tap is not recorded in either touchesBegan or touchesMoved. But if I implement my touch handling in the UIViews themselves, once a view registers a touch, if the user does not lift their finger up and moves it, the views around the first view do not register the touch. Only if the user lifts his finger and then puts it back down will the surrounding views register the touch.
So my question is, lets say I have two views side by side, my touch handling code is implemented in the views, and I put my finger down on view 1. I then slide my finger over to view 2 - what do I need to do to make view 2 register that touch, which started in view 1 and never "ended"?
Set userInteractionEnabled property of UIView to NO.
view.userInteractionEnabled = NO;
UIView has the following property:
#property(nonatomic, getter=isUserInteractionEnabled) BOOL userInteractionEnabled
Ok, I figured out what was going on. Thing is, I have my views as subviews of a scrollview, which is itself a subview of my main view. With scrollEnabled = NO, I could touch my subviews - but apparently the scrollview was only forwarding me the initial touch event, and all subsequent touches were part of that initial event. Because of that, I had many weird problems such as touching two views one after the other, both would select and highlight, but if I took the first finger off the screen both views would de-select. This was not the desired behavior.
So what I did is I subclassed the scrollview and overrode the touch handling methods to send the events to its first responder, which is its superview, which is the view where I'm doing my touch handling. Now it works!
I have a UITableView with Custom Cells, when you touch the cell it adds a subview of a UIImageView.
I want the image to disappear after the user lifts there finger and i have a touchesEnded method on the UIImageVIew Subview but its never called.
It is only called if you lift your finger then press it down again and release it.
How do I get the method to be called on the original touch ended.
Im kinda going for what Snapchat does when you view images.
The reason the subview does not get the touchesEnded event is that it has not received the touchesBegan event: these two come in pairs - whichever view gets the touches began is going to get the touches ended. Your UIImageView could not get touchesEnded because it wasn't there at the time; it gets touchesEnded the second time around when you press down and release because it's there for both events.
There are several ways around this problem:
Process view removal in the same place where you process the addition of UIImageView - when you add the subview, store a __weak reference to it in a separate variable. When the view that added the UIImageView gets the touchesEnded event, go to that variable, and remove subview.
Keep UIImageView there, but control its transparency - rather than adding and removing the subvuew, start it as fully transparent, then make it opaque on touch, then make it transparent again on release.
Don't add the temporary UIImageView at all, use CALayer instead - it looks like you are adding the image view simply to host an image in it for a short time. There is a simpler way of doing it that's much lighter-way - using CALayer. This approach should be easier to implement, because the layer does not participate in handling of touch events.
Presumably the touchBegan method is on the Cell. That's when the subview gets added.
The touch has already been recorded by the Cell. The Cell's touchesEnded is what's going to be called when you lift up your finger. So that's where you need to handle removing the subview from the screen. Save a reference to the subview in the cell class, and if it is not equal to nil, remove it on touchesEnded. Simple as that.
The touch began on the Cell, before the subview existed. That same touch is going to end on the cell. You can't transfer the touch to another view while it's in progress.
When you touch that view and raise your finger, you should not drag your finger. If you did like that, then touchesCancelled: method will get called. So I think your view is too small to touch. If yes, then make a big View and try it again. It will work for you then.
I need to capture the following touch events on iOS and interpret them:
Finger A touch down within a UIButton (stays down)
Finger B performs a pan gesture elsewhere on the screen, providing continuous callbacks. (I plan to use a UIPanGestureRecognizer to implement this functionality).
Finger A touch up within the same UIButton
In essence, pressing and holding the UIButton puts the app into a special mode which lasts only as long as the button is held. Pan gestures performed during this time do different things to when the button is not pressed.
What I've tried so far:
hooking up the UIButton Touch Down and Touch Up Inside to IBActions in my UIViewController subclass. I've also
Problem encountered: the Touch Up Inside action is not called when another gesture happens on the screen from another finger while the button is pressed. This is because the original touch is cancelled.
attaching a UITapGestureRecognizer to the UIButton
Problem encountered: This only fires when the finger leaves the screen, hence it cannot be used to put the app into a special mode while the button is pressed.
I need to use a UIButton rather than a UIView so that the button has correct highlighting behaviour when pressed.
What is the best overall approach, given the problems I've encountered so far?
Use UILongPressGestureRecognizer with short minimumPressDuration on the "button"
http://developer.apple.com/library/ios/#documentation/uikit/reference/UILongPressGestureRecognizer_Class/Reference/Reference.html#//apple_ref/occ/cl/UILongPressGestureRecognizer
Important thing is to use gesture recognizer delegate to make sure that gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: returns YES for these recognizers. Then, when your UIPanGestureRecognizer calls the event handler, you can check the state of your UILongPressGestureRecognizer and if the button isn't pressed just ignore the pan gesture.
Imagine a UIButton and a user who taps somewhere OUTSIDE the button and then slides onto it. If I want the button to detect such touches I can register it for any "Touch Drag Enter" events.
My question: is there some way to achieve the same for a UIScrollView?
I.e., I tap somewhere outside the scrollview, drag my finger onto the scrollview and as soon as I enter the scrollviews frame it starts panning? (Because by default it doesn't, I have to start my touch INSIDE the scrollview in order to pan)
If you want to do this, you will have to do a custom implementation using -touchesBegan, -touchesMoved, and -touchesEnded
The documentation for the UIResponder class (which all ViewControllers inherit from) that allows you to do this is here.
Hopefully you can figure out what to do from here. :)
As an extra hint, you will also most likely need to use this function
bool contains = CGRectContainsPoint(CGRect rect, CGPoint point);
Imagine a UIButton and a user who taps somewhere OUTSIDE the button and then slides onto it. If I want the button to detect such touches I can register it for any "Touch Drag Enter" events
Actually, no you can't. A UIControl will not get any of the control events unless the touch started in the control.
And that's for the same reason that you are seeing a similar effect with the scroll view. This is the entire basis of touch delivery on iOS. A touch "belongs" to the view that was initially touched.
After all, the runtime cannot possibly know that you are going to drag into the scroll view...