Trigger touchesEnded when dragging outside the UIView - ios

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.

Related

What gets called instead of - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event when Accessibility(voice over is on)

Recently I have put a breakpoint in a UIViews method
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
}
method and checked if the compiler stops here when a user taps on the UIView while voiceover is on, but it never came to the breakpoint, does anyone know what gets called and how the touch can be intercepted?
The standard hitTest mechanism is not used when VoiceOver is on. Instead, UIView has an _accessibilityHitTest:withEvent: method, but unlike macOS, it is private and can't easily be overridden or called.
Similar to hitTest, _accessibilityHitTest uses _accessibilityPointInside:withEvent:, which, in turn, calls pointInside:withEvent: (which is public).
First of all, note that users must double-tap to "activate" or "tap" a view when VoiceOver is enabled. If you still aren't hitting hitTest:…, then break on acccessibilityActivate(). This is the default accessibility action triggered by a double-tap. You may also be interested in the activationPoint, which is the default location of the simulated touch VoiceOver emits upon activation. Note that the activation point isn't relevant to all VoiceOver interactions (eg. adjustable controls).
The hit-test view is given the first opportunity to handle a touch event. If the hit-test view cannot handle an event, the event travels up that view’s chain of responders as described in “The Responder Chain Is Made Up of Responder Objects” until the system finds an object that can handle it. Please look at this.

Subclassing UIControl's endTracking for multitouch

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.

How to use touches began?

So I'm writing an app in which I have a custom UIView which contains a UIScrollView which contains a series of UIImageViews. I want to make it so that when I touch one of the UIImageViews within the UIScrollView, an event happens (for now, let's just say I print an NSLog() or something).
I know that there exists this function:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
But I'm not entirely sure how to use it. Which UIView should implement this function? Where would I call it exactly? Or does it get called automatically when something is touched? How do I find the correct UIImageView?
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event is already implemented by UIView (actually implemented by the UIResponder superclass, and by default it does nothing). this method is there for you to override in subclasses to do whatever you want to do.
this method is indeed called automatically whenever touches begin within the views boundary.
In your case using a UIButton with an image may be better.
instead of using touchesBegin: , subclass UIImageView, add Tap gesture to this new Class and use this Class instead of the ImageViews you are using on scrollView.

Is there a way to register all touchEnded events, no matter how long the touch was?

Is there a way to register all touch up through touchesEnded. It will only fire when there is a tap that lasts more than a second or two. Is there away to have it fire on all touch ups? It registers all touchesBegan.
Here is the simple code:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Touch Ended");
}
-touchesEnded:withEvent: gets called even for brief touches, like taps. You probably have something in your responder chain that's handling the touch. For example, scroll views will normally delay touches until they figure out whether the user is trying to scroll, and gesture recognizers can also delay touch events. Specifically, UIGestureRecognizer has a delaysTouchesEnded property that may be interfering with your code.
Multiple touches are disabled by default. If you want to receive multiple touch events you must set the a multipleTouchEnabled to YES of the view which you use. To do this in your ViewDidLoad just write self.view.multipleTouchEnabled= YES because it's just a property. Another issue may be connected with Magic Trackpad because it adds a delay before it decides that you have ended a touch.Try to disable it : >System Preferences>Personal>Universal Access>Mouse & Trackpad>Trackpad Options>Ignore Trackpad when mouse is present.

how to touch on animated object iOS

For animation my UIImageView object I use these approaches:
Block animation
Begin/Commit method for animation UIView based object
The problem is next: When I try to tap on some object on my view it does not response to my action. Being animatable the object don't receive any calls from the main thread.
In my UIViewController I have method below that should handle touched object. All objects receive touch events but the objects which are using in animation block don't receive any events.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Anybody knows what is the probem here?
In order for an animated view to recieve user events, UIViewAnimationOptionAllowUserInteraction must be passed into the options parameter.

Resources